Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Base / System / IO / Packaging / CompoundFile / DataSpaceManager.cs / 1 / DataSpaceManager.cs
//------------------------------------------------------------------------------ // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // The object for manipulating data spaces within the WPP Package. // // History: // 06/04/2002: RogerCh: Initial creation of class & method stubs. // 06/17/2002: RogerCh: Initial implementation. // 05/20/2003: RogerCh: Ported to WCP tree. // 05/28/2003: RogerCh: Added long name support // //----------------------------------------------------------------------------- using System; using System.Collections; using System.Diagnostics; // For Debug.Assert using System.Globalization; using System.IO; using System.Reflection; // For finding transform objects & their constructor using System.Security.Permissions; // For StrongNameIdentityPermission using System.Collections.Generic; using System.Windows; // ExceptionStringTable using MS.Internal.IO.Packaging; using MS.Internal.IO.Packaging.CompoundFile; using CU = MS.Internal.IO.Packaging.CompoundFile.ContainerUtilities; namespace System.IO.Packaging { ////// This class is used to manipulate the data spaces within a specific instance /// of the Avalon container. This is how data transform modules are plugged /// into the container to enable features like data compression and data /// encryption. /// internal class DataSpaceManager { /***********************************************************************/ // Constants // The header bytes that this version understands and supports const int KnownBytesInMapTableHeader = 8; // Two Int32s == 8 bytes const int KnownBytesInDataSpaceDefinitionHeader = 8; const int KnownBytesInTransformDefinitionHeader = 8; const int AllowedExtraDataMaximumSize = 8192; // 8K // Names for streams and storages within the container const string DataSpaceStorageName = "\x0006DataSpaces"; const string DataSpaceVersionName = "Version"; const string DataSpaceMapTableName= "DataSpaceMap"; const string DataSpaceDefinitionsStorageName = "DataSpaceInfo"; const string TransformDefinitions = "TransformInfo"; const string TransformPrimaryInfo = "\x0006Primary"; // The string used in FormatVersion private static readonly string DataSpaceVersionIdentifier = "Microsoft.Container.DataSpaces"; // Version Writer - 1.0, Reader - 1.0, Updater - 1.0 private static readonly VersionPair DataSpaceCurrentWriterVersion = new VersionPair(1 /*major*/, 0 /*minor*/); private static readonly VersionPair DataSpaceCurrentReaderVersion = new VersionPair(1 /*major*/, 0 /*minor*/); private static readonly VersionPair DataSpaceCurrentUpdaterVersion = new VersionPair(1 /*major*/, 0 /*minor*/); // The version information we read from the file private FormatVersion _fileFormatVersion; private bool _dirtyFlag; /***********************************************************************/ // Data space manager instance values ////// There is only one data space manager per container instance. This /// points back to "our" reference. /// StorageRoot _associatedStorage; ////// Maps container references to data spaces /// /// Keys into this list are CompoundFileReference instances, each /// representing a subset of the container that is encoded with a /// particular data space. /// /// Values are strings, which are data space labels and can be used /// as keys into _dataSpaceDefinitions for more details /// SortedList _dataSpaceMap; ////// Extra data in the data space mapping table header is preserved /// in this byte array. /// byte[] _mapTableHeaderPreservation; ////// Maps a data space name to a string array of transform names. /// /// Keys into this hash table are strings, each a unique label for /// a data space. /// /// Values from this hash table are ArrayLists, each an array of /// strings. Each string is a data space label. This transform /// stack is stored in bottom-up order. The first transform listed /// is the first to get the raw bytes from disk. /// Hashtable _dataSpaceDefinitions; ////// Maps a transform name to an instance of transform handle class /// /// Keys into this hash table are strings, each a unique label for /// a transform object instance. /// /// Values from this hash table are references to the TransformInstance /// class defined below, each of which contains information for a /// particular transform instance. /// Hashtable _transformDefinitions; ////// When shutting down, we need to flush each open transformed stream in /// order to ensure that all encoding data has been propagated through /// the transform stack before we shut things down. Otherwise we may leave /// data in a state where it could not be written out because parts of the /// transform stack has already been disposed. /// ArrayList _transformedStreams; ////// Table of "well-known" -- that is, "built-in" -- transforms. The keys are /// the TransformClassName identifier strings for the well-known transforms, /// such as encryption and compression. The values are the assembly-qualified /// .NET class names of the classes that implement the transforms. /// static readonly Hashtable _transformLookupTable; /***********************************************************************/ // Private class for tracking individual transform instances private class TransformInstance { // When we only know the CLR type name internal TransformInstance( int classType, string name ) : this(classType, name, null, null, null, null ) {;} // When we also have an actual object in memory and its associated // environment object internal TransformInstance( int classType, string name, IDataTransform instance, TransformEnvironment environment ) : this(classType, name, instance, environment, null, null ) {;} // When we know everything to put into a TransformInstance. internal TransformInstance( int classType, string name, IDataTransform instance, TransformEnvironment environment, Stream primaryStream, StorageInfo storage ) { typeName = name; transformReference = instance; transformEnvironment = environment; transformPrimaryStream = primaryStream; transformStorage = storage; _classType = classType; } internal byte[] ExtraData { get { return _extraData; } set { _extraData = value; } } internal int ClassType { get { return _classType; } } ////// This is the CLR name used to define this transform. Keep in /// mind that this is not necessarily what we retrieve when we /// call Type.FullName or Type.AssemblyQualifiedName. This is /// the name that needs to be persisted in the file because it is /// what the caller told us to use to find the type. /// internal string typeName; ////// If we have actually created the transform object, we keep /// a reference to it here. This may be null if we haven't /// had a need to create the transform object. /// internal IDataTransform transformReference; ////// The instance of TransformEnvironment that we created and /// handed off to the transform object to tell us about things. /// This can also be null, but only when transformReference is /// null. It is not valid for only one of these two to be null, /// either they're both null or they're both non-null. /// internal TransformEnvironment transformEnvironment; ////// The stream that is the primary data storage for this instance /// of the transform object. /// internal Stream transformPrimaryStream; ////// The storage that is available if the transform object requires /// more than the primary stream /// internal StorageInfo transformStorage; private int _classType; private byte[] _extraData; } private class DirtyStateTrackingStream: Stream { //////////////////////////////////// // Stream section ///////////////////////////////// public override bool CanRead { get { return (_baseStream != null && _baseStream.CanRead); } } public override bool CanSeek { get { return (_baseStream != null && _baseStream.CanSeek); } } public override bool CanWrite { get { return (_baseStream != null && _baseStream.CanWrite); } } public override long Length { get { CheckDisposed(); return _baseStream.Length; } } public override long Position { get { CheckDisposed(); return _baseStream.Position; } set { CheckDisposed(); _baseStream.Position = value; } } public override void SetLength(long newLength) { CheckDisposed(); if (newLength != _baseStream.Length) { _dirty = true; } _baseStream.SetLength(newLength); } public override long Seek(long offset, SeekOrigin origin) { CheckDisposed(); return _baseStream.Seek(offset, origin); } public override int Read(byte[] buffer, int offset, int count) { CheckDisposed(); return _baseStream.Read(buffer, offset, count); } public override void Write(byte[] buffer, int offset, int count) { CheckDisposed(); _baseStream.Write(buffer, offset, count); _dirty = true; } public override void Flush() { CheckDisposed(); _baseStream.Flush(); } ///////////////////////////// // Internal Constructor ///////////////////////////// internal DirtyStateTrackingStream(Stream baseStream) { Debug.Assert(baseStream != null); _baseStream = baseStream; } internal bool DirtyFlag { get { return (_baseStream != null && _dirty); } } internal Stream BaseStream { get { return _baseStream; } } //----------------------------------------------------- // // Protected Methods // //----------------------------------------------------- ////// Dispose(bool) /// /// ///We implement this because we want a consistent experience (essentially Flush our data) if the user chooses to /// call Dispose() instead of Close(). protected override void Dispose(bool disposing) { try { if (disposing) { if (_baseStream != null) _baseStream.Close(); } } finally { _baseStream = null; base.Dispose(disposing); } } ///////////////////////////// // Private Methods ///////////////////////////// private void CheckDisposed() { if (_baseStream == null) { throw new ObjectDisposedException(null, SR.Get(SRID.StreamObjectDisposed)); } } private bool _dirty; private Stream _baseStream; } private struct DataSpaceDefinition { ArrayList _transformStack; Byte[] _extraData; internal DataSpaceDefinition(ArrayList transformStack, Byte[] extraData) { _transformStack = transformStack; _extraData = extraData; } internal ArrayList TransformStack { get { return _transformStack; } } internal Byte[] ExtraData { get { return _extraData; } } } /***********************************************************************/ // Constructors ////// Static constructor that initializes the transformLookupTable (which see). /// static DataSpaceManager() { // Transform Identifier: we preserve casing, but do case-insensitive comparison _transformLookupTable = new Hashtable(CU.StringCaseInsensitiveComparer); _transformLookupTable[RightsManagementEncryptionTransform.ClassTransformIdentifier] = "System.IO.Packaging.RightsManagementEncryptionTransform"; _transformLookupTable[CompressionTransform.ClassTransformIdentifier] = "System.IO.Packaging.CompressionTransform"; } ////// Internally visible constructor /// /// The container instance we're associated with internal DataSpaceManager( StorageRoot containerInstance ) { _associatedStorage = containerInstance; // Storage under which all data space information is stored. StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); // Initialize internal data structures. // _dataSpaceMap = new SortedList(); _mapTableHeaderPreservation = new byte[0]; _dataSpaceDefinitions = new Hashtable(CU.StringCaseInsensitiveComparer); _transformDefinitions = new Hashtable(CU.StringCaseInsensitiveComparer); _transformedStreams = new ArrayList(); // Check to see if we have any data space information to read if (dataSpaceStorage.Exists) { // Read any existing data space mapping information from the container // ReadDataSpaceMap(); ReadDataSpaceDefinitions(); ReadTransformDefinitions(); } return; } ////// Returns the number of data spaces defined in this manager object /// internal int Count { get { CheckDisposedStatus(); return _dataSpaceMap.Count; } } private bool DirtyFlag { get { if (_dirtyFlag) // It is already dirty don't need to check further return true; foreach( string transformDef in _transformDefinitions.Keys ) { TransformInstance transformInstance = GetTransformInstanceOf( transformDef ); if (((DirtyStateTrackingStream) transformInstance.transformPrimaryStream).DirtyFlag) { return true; } } return false; } } ////// Clean up the data space information and flush all data to the container. /// public void Dispose() { CheckDisposedStatus(); // Flush any outstanding data in the transformed streams foreach( StreamWithDictionary dataStream in _transformedStreams ) { if (!dataStream.Disposed) dataStream.Flush(); } _transformedStreams.Clear(); // Now that all data have been flushed, shut down the transform // objects. foreach( object o in _transformDefinitions.Values ) { IDataTransform idt = ((TransformInstance)o).transformReference; if( null != idt && null != (idt as IDisposable)) { ((IDisposable)idt).Dispose(); } } // Now write the tables out. // if (FileAccess.Read != _associatedStorage.OpenAccess && DirtyFlag) { WriteDataSpaceMap(); WriteDataSpaceDefinitions(); WriteTransformDefinitions(); } _dataSpaceMap = null; _dataSpaceDefinitions = null; _transformDefinitions = null; return; } ////// Removes the container reference from the dataspace map. This is called when the container/substorage is getting deleted from the root storage. [DeleteSubStorage in StorageInfo] /// internal void RemoveContainerFromDataSpaceMap(CompoundFileReference target) { CheckDisposedStatus(); if (_dataSpaceMap.Contains(target)) { _dataSpaceMap.Remove(target); _dirtyFlag = true; } } // Check to see if the dispose method has been called. If so, throw an // ObjectDisposedException. internal void CheckDisposedStatus() { // First check root _associatedStorage.CheckRootDisposedStatus(); // Check if we've been disposed if( null == _dataSpaceMap ) { Debug.Assert( null == _dataSpaceDefinitions, "Having a null data space map and a non-null data space definitions map is an inconsistent state" ); Debug.Assert( null == _transformDefinitions, "Having a null data space map and a non-null transform definition map is an inconsistent state" ); throw new ObjectDisposedException(null, SR.Get(SRID.DataSpaceManagerDisposed)); } } ////// Define a data space with the given stack of transform objects and /// labeled with the given name. The transform stack is interpreted in /// bottom-up order. (Clear-text transform is last.) /// /// Transform stack /// New data space label internal void DefineDataSpace( string[] transformStack, string newDataSpaceLabel ) { CheckDisposedStatus(); // Data space must have at least one transform if( null == transformStack || 0 == transformStack.Length ) throw new ArgumentException( SR.Get(SRID.TransformStackValid)); // Given label must be a non-empty string CU.CheckStringAgainstNullAndEmpty(newDataSpaceLabel, "newDataSpaceLabel"); // Given label must not be a reserved string CU.CheckStringAgainstReservedName(newDataSpaceLabel, "newDataSpaceLabel"); // Given label must not already be in use if( DataSpaceIsDefined( newDataSpaceLabel ) ) throw new ArgumentException( SR.Get(SRID.DataSpaceLabelInUse)); // Given transform array must include labels that have already been defined foreach( string transformLabel in transformStack ) { CU.CheckStringAgainstNullAndEmpty( transformLabel, "Transform label" ); if( !TransformLabelIsDefined( transformLabel ) ) throw new ArgumentException( SR.Get(SRID.TransformLabelUndefined)); } // Passes all inspection, data space definition successful. SetDataSpaceDefinition( newDataSpaceLabel, new DataSpaceDefinition(new ArrayList(transformStack), null)); _dirtyFlag = true; return; } ////// Internal shortcut to check if data space is defined. When we start /// doing on-demand reads of data space definitions, the "demand" could /// be triggered by this. /// /// Label to check ///True if label is in use internal bool DataSpaceIsDefined( string dataSpaceLabel ) { CU.CheckStringAgainstNullAndEmpty(dataSpaceLabel, "dataSpaceLabel"); return _dataSpaceDefinitions.Contains( dataSpaceLabel ); } ////// Central place to set a data space definition, centralizing the /// call to the name manager. /// private void SetDataSpaceDefinition( string dataSpaceLabel, DataSpaceDefinition definition ) { _dataSpaceDefinitions[ dataSpaceLabel ] = definition; } ////// Central place to get a data space definition, centralizing the /// call to the name manager. /// private DataSpaceDefinition GetDataSpaceDefinition( string dataSpaceLabel ) { return ((DataSpaceDefinition) _dataSpaceDefinitions[dataSpaceLabel]); } ////// Internal method to retrieve the data space label corresponding to /// a container reference. Returns null if no data space is associated. /// /// CompoundFileReference whose data space label is to be retrieved ///Data space label internal string DataSpaceOf( CompoundFileReference target ) { // Can't simply return _dataSpaceMap[target] because I need to cast it // into a string, and if it's null the cast `blows up. if( _dataSpaceMap.Contains(target) ) { return (string)_dataSpaceMap[target]; } else { return null; } } ////// This method returns all the transforms that are applied to a particular stream as an /// List of IDataTransform objects. /// /// StreamInfo for the stream whose transforms are requested ///A List of IDataTransform objects that are applied to the stream represented by streamInfo internal ListGetTransformsForStreamInfo(StreamInfo streamInfo) { string dataSpaces = this.DataSpaceOf(streamInfo.StreamReference); if (dataSpaces == null) // No datas pace is associated with the stream { return new List (0); // return an empty list } ArrayList transformsList = this.GetDataSpaceDefinition(dataSpaces).TransformStack; List dataTransforms = new List (transformsList.Count); for (int i = 0; i < transformsList.Count; i++) { dataTransforms.Add(this.GetTransformFromName(transformsList[i] as string)); } return dataTransforms; } /// /// Define a data space with the given stack of transform objects and /// labeled with an auto-generated name /// /// Transform stack ///The auto-generated label for this data space internal string DefineDataSpace( string[] transformStack ) { CheckDisposedStatus(); Int64 timeSeed = DateTime.Now.ToFileTime(); string generatedName = timeSeed.ToString(CultureInfo.InvariantCulture); // If there is a name collision, just keep incrementing the number while( DataSpaceIsDefined( generatedName ) ) { timeSeed++; generatedName = timeSeed.ToString(CultureInfo.InvariantCulture); } // Submit the definition DefineDataSpace( transformStack, generatedName ); return generatedName; } ////// Tranform object is created. We are no longer using reflection to do this. We are supporting limited data transforms. /// private IDataTransform InstantiateDataTransformObject(int transformClassType, string transformClassName, TransformEnvironment transformEnvironment ) { object transformInstance = null; if (transformClassType != (int) TransformIdentifierTypes_PredefinedTransformName) throw new NotSupportedException(SR.Get(SRID.TransformTypeUnsupported)); // Transform Identifier: we preserve casing, but do case-insensitive comparison if (((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(transformClassName, RightsManagementEncryptionTransform.ClassTransformIdentifier)) { transformInstance = new RightsManagementEncryptionTransform( transformEnvironment); } else if (((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(transformClassName, CompressionTransform.ClassTransformIdentifier)) { transformInstance = new CompressionTransform( transformEnvironment ); } else { //this transform class is not supported. Need to change this to appropriate error. throw new ArgumentException( SR.Get(SRID.TransformLabelUndefined)); } if (null != transformInstance) { if( !( transformInstance is IDataTransform ) ) throw new ArgumentException( SR.Get(SRID.TransformObjectImplementIDataTransform)); return (IDataTransform)transformInstance; } return null; } ////// Private method to check if a transform label is defined. When we /// start reading transform defintions on-demand, we would probably do it /// here as necessary. /// /// Transform label to check ///True if label is defined in hash table internal bool TransformLabelIsDefined( string transformLabel ) { // return _transformDefinitions.Contains( transformLabel ); } ////// Central place to set a transform definition, centralizing the /// call to the name manager. /// private void SetTransformDefinition( string transformLabel, TransformInstance definition ) { _transformDefinitions[ transformLabel ] = definition; } ////// Private method to get the TransformInstance class representing /// a transform instance. /// private TransformInstance GetTransformInstanceOf( string transformLabel ) { Debug.Assert( TransformLabelIsDefined( transformLabel ), "Data space manager caller failed to verify transform exists before retrieving instance" ); // return _transformDefinitions[ transformLabel ] as TransformInstance; } ////// Internal method to get a MemoryStream whose contents will be /// stored in the "\x0006Primary" data stream after our type identification /// information /// /// Transform Label ///Memory stream object for transform instance primary stream internal Stream GetPrimaryInstanceStreamOf( string transformLabel ) { TransformInstance targetInstance = GetTransformInstanceOf( transformLabel ); if( null == targetInstance.transformPrimaryStream ) { //build memory stream on the byte[0] , and allow writes only if // FileAccess is Write or ReadWrite if (_associatedStorage.OpenAccess == FileAccess.Read) { targetInstance.transformPrimaryStream = new DirtyStateTrackingStream (new MemoryStream (new byte[0], false /* Not writable */)); } else { targetInstance.transformPrimaryStream = new DirtyStateTrackingStream (new MemoryStream()); } } return targetInstance.transformPrimaryStream; } ////// Internal method to get the StorageInfo where the transform instance /// data is stored. This StorageInfo may not yet exist! /// /// Transform Label ///StorageInfo pointing to transform instance data storage internal StorageInfo GetInstanceDataStorageOf( string transformLabel ) { TransformInstance targetInstance = GetTransformInstanceOf( transformLabel ); if( null == targetInstance.transformStorage ) { //string name = DataSpaceStorageName + '\\' + TransformDefinitions + '\\' + transformLabel; //targetInstance.transformStorage = new StorageInfo(_associatedStorage,name); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); if (!dataSpaceStorage.Exists ) { dataSpaceStorage.Create(); } StorageInfo transformDefinition = new StorageInfo( dataSpaceStorage, TransformDefinitions ); if (!transformDefinition.Exists) { transformDefinition.Create(); } targetInstance.transformStorage = new StorageInfo(transformDefinition,transformLabel); } return targetInstance.transformStorage; } ////// Internal method to get the transform object corresponding to the specified /// transform instance label. The transform object is created if it does not exist. /// /// /// String that identifies the transform instance. /// ////// An IDataTransform interface pointer to the transform object identified by /// internal IDataTransform GetTransformFromName(string transformLabel) { TransformInstance transformInstance = _transformDefinitions[transformLabel] as TransformInstance; if (transformInstance == null) { // // There is no transform instance with the specified name. // return null; } IDataTransform transformObject = transformInstance.transformReference; if (transformObject == null) { // // There is a transform instance with the specified name, but its transform // object has not yet been created. Create it now. This code is modeled on the // code in DefineTransform. // TransformEnvironment transformEnvironment = new TransformEnvironment(this, transformLabel); // Create the transform object. transformObject = InstantiateDataTransformObject( transformInstance.ClassType, transformInstance.typeName, transformEnvironment); transformInstance.transformReference = transformObject; } return transformObject; } ///, or null if there is no such transform. /// /// Define a data transform object with the given object identification and /// labeled with the given name. /// /// Transform identification string /// Label to use for new transform internal void DefineTransform(string transformClassName, string newTransformLabel ) { CheckDisposedStatus(); // Check to see if transform name is obviously invalid CU.CheckStringAgainstNullAndEmpty( transformClassName, "Transform identifier name" ); // Check to see if transform name is valid CU.CheckStringAgainstNullAndEmpty( newTransformLabel, "Transform label" ); // Given transform name must not be a reserved string CU.CheckStringAgainstReservedName( newTransformLabel, "Transform label" ); // Can't re-use an existing transform name if( TransformLabelIsDefined( newTransformLabel ) ) throw new ArgumentException( SR.Get(SRID.TransformLabelInUse)); // Create class the transform object will use to communicate to us TransformEnvironment transformEnvironment = new TransformEnvironment( this, newTransformLabel ); // Create a TransformInstance object to represent this transform instance. TransformInstance newTransform = new TransformInstance( TransformIdentifierTypes_PredefinedTransformName, transformClassName, null, transformEnvironment ); SetTransformDefinition( newTransformLabel, newTransform ); // Create the transform object IDataTransform transformObject = InstantiateDataTransformObject( TransformIdentifierTypes_PredefinedTransformName, transformClassName, transformEnvironment ); newTransform.transformReference = transformObject; // If transform is not ready out-of-the-box, do an initialization run. // Note: Transform is not required to be "ready" after this. This is // done for those transforms that need initialization work up-front. if( ! transformObject.IsReady ) { CallTransformInitializers( new TransformInitializationEventArgs( transformObject, null, null, newTransformLabel) ); } _dirtyFlag = true; return; } ////// Define a data transform object with the given object identification and /// labeled with an auto-generated name. /// /// Transform identification string ///The auto-generated label for this transform internal string DefineTransform( string transformClassName ) { CheckDisposedStatus(); Int64 timeSeed = DateTime.Now.ToFileTime(); string generatedName = timeSeed.ToString(CultureInfo.InvariantCulture); // If there is a name collision, just keep incrementing the number while( TransformLabelIsDefined( generatedName ) ) { timeSeed++; generatedName = timeSeed.ToString(CultureInfo.InvariantCulture); } // Submit the definition DefineTransform( transformClassName, generatedName ); return generatedName; } //+---------------------------------------------------------------------- // Transform initialization event/delegate/etc. ////// Delegate method for initializing transforms /// internal delegate void TransformInitializeEventHandler( object sender, TransformInitializationEventArgs e ); ////// Transform initialization event /// internal event TransformInitializeEventHandler OnTransformInitialization; ////// Internal shortcut to kick off all the initializers /// /// Arguments for the initializers internal void CallTransformInitializers( TransformInitializationEventArgs initArguments ) { if( null != OnTransformInitialization ) OnTransformInitialization( this, initArguments ); } ////// Reads a data space map from the associated container, if such a thing /// is written to the file. /// void ReadDataSpaceMap() { // See if there's even a data spaces storage StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); StreamInfo dataSpaceMapStreamInfo = new StreamInfo( dataSpaceStorage, DataSpaceMapTableName ); if( dataSpaceStorage.StreamExists(DataSpaceMapTableName) ) { // There is an existing data space mapping table to read. // Read the versioning information ReadDataSpaceVersionInformation(dataSpaceStorage); // Check if its the correct version for reading ThrowIfIncorrectReaderVersion(); // Read the data space mapping table using(Stream dataSpaceMapStream = dataSpaceMapStreamInfo.GetStream(FileMode.Open)) { using(BinaryReader dataSpaceMapReader = new BinaryReader( dataSpaceMapStream, System.Text.Encoding.Unicode )) { int headerLength = dataSpaceMapReader.ReadInt32(); int entryCount = dataSpaceMapReader.ReadInt32(); if (headerLength < KnownBytesInMapTableHeader || entryCount < 0) throw new FileFormatException(SR.Get(SRID.CorruptedData)); int extraDataSize = headerLength - KnownBytesInMapTableHeader; if( 0 < extraDataSize ) { if (extraDataSize > AllowedExtraDataMaximumSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); _mapTableHeaderPreservation = dataSpaceMapReader.ReadBytes(extraDataSize); if (_mapTableHeaderPreservation.Length != extraDataSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); } _dataSpaceMap.Capacity = entryCount; int entryLength; int bytesRead; int totalBytesRead; for( int i = 0; i < entryCount; i++ ) { entryLength = dataSpaceMapReader.ReadInt32(); if (entryLength < 0) throw new FileFormatException(SR.Get(SRID.CorruptedData)); totalBytesRead = 4; // entryLength // Read the container reference entry CompoundFileReference entryRef = CompoundFileReference.Load( dataSpaceMapReader, out bytesRead ); checked { totalBytesRead += bytesRead; } // Read data space string and add to data space mapping table string label = CU.ReadByteLengthPrefixedDWordPaddedUnicodeString(dataSpaceMapReader, out bytesRead); checked { totalBytesRead += bytesRead; } _dataSpaceMap[entryRef] = label; // Verify entryLength against what was actually read: if (entryLength != totalBytesRead) { throw new IOException(SR.Get(SRID.DataSpaceMapEntryInvalid)); } } } } } } ////// Write the data space mapping table to underlying storage. /// void WriteDataSpaceMap() { ThrowIfIncorrectUpdaterVersion(); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); StreamInfo dataSpaceMapStreamInfo = new StreamInfo ( dataSpaceStorage, DataSpaceMapTableName ); if( 0 < _dataSpaceMap.Count ) { // Write versioning information StreamInfo versionStreamInfo = null; if (dataSpaceStorage.StreamExists( DataSpaceVersionName ) ) versionStreamInfo = dataSpaceStorage.GetStreamInfo( DataSpaceVersionName ); else versionStreamInfo = dataSpaceStorage.CreateStream( DataSpaceVersionName ); Stream versionStream = versionStreamInfo.GetStream(); _fileFormatVersion.SaveToStream(versionStream); versionStream.Close(); // Create stream for write, overwrite any existing using(Stream dataSpaceMapStream = dataSpaceMapStreamInfo.GetStream(FileMode.Create)) { using(BinaryWriter dataSpaceMapWriter = new BinaryWriter( dataSpaceMapStream, System.Text.Encoding.Unicode )) { // Write header // header length = our known size + preserved array size dataSpaceMapWriter.Write( checked ((Int32) (KnownBytesInMapTableHeader + _mapTableHeaderPreservation.Length))); // number of entries dataSpaceMapWriter.Write( _dataSpaceMap.Count ); // anything else we've preserved dataSpaceMapWriter.Write( _mapTableHeaderPreservation ); // Loop to write entries foreach( CompoundFileReference o in _dataSpaceMap.Keys ) { // determine the entry length string label = (string)_dataSpaceMap[o]; int entryLength = CompoundFileReference.Save(o, null); checked { entryLength += CU.WriteByteLengthPrefixedDWordPaddedUnicodeString(null, label); } // length of entryLength itself checked { entryLength += 4; } // write out the entry length dataSpaceMapWriter.Write((Int32) entryLength); // Write out reference CompoundFileReference.Save( o, dataSpaceMapWriter); // Write out dataspace label CU.WriteByteLengthPrefixedDWordPaddedUnicodeString( dataSpaceMapWriter, label); } } } } else { // data space map is empty, remove existing stream if there. if ( dataSpaceStorage.StreamExists( DataSpaceMapTableName ) ) dataSpaceStorage.DeleteStream( DataSpaceMapTableName ); } } ////// Read all data space definitions in one chunk. To be replaced /// with on-demand reading mechanism. /// void ReadDataSpaceDefinitions() { ThrowIfIncorrectReaderVersion(); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); StorageInfo dataSpaceDefinitionsStorage = new StorageInfo( dataSpaceStorage, DataSpaceDefinitionsStorageName ); if( dataSpaceDefinitionsStorage.Exists ) { // Fill in the Data Space Definitions hash table foreach( StreamInfo definitionStreamInfo in dataSpaceDefinitionsStorage.GetStreams()) { // Open up the stream for this data space definition using(Stream definitionStream = definitionStreamInfo.GetStream(FileMode.Open)) { using(BinaryReader definitionReader = new BinaryReader( definitionStream, System.Text.Encoding.Unicode )) { // Read data space definition stream int headerLength = definitionReader.ReadInt32(); int transformCount = definitionReader.ReadInt32(); if (headerLength < KnownBytesInDataSpaceDefinitionHeader || transformCount < 0) throw new FileFormatException(SR.Get(SRID.CorruptedData)); ArrayList transformLabels = new ArrayList(transformCount); byte[] extraData = null; int extraDataSize = headerLength - KnownBytesInDataSpaceDefinitionHeader; if (extraDataSize > AllowedExtraDataMaximumSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); if (extraDataSize > 0) { extraData = definitionReader.ReadBytes(extraDataSize); if (extraData.Length != extraDataSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); } // Read the array of strings that make up the transform stack for( int i = 0; i < transformCount; i++ ) { transformLabels.Add( CU.ReadByteLengthPrefixedDWordPaddedUnicodeString( definitionReader ) ); } // Add data space definition to table SetDataSpaceDefinition( definitionStreamInfo.Name, new DataSpaceDefinition(transformLabels, extraData)); } } } } } ////// Write all data space definitions to underlying storage in one chunk. /// /// // void WriteDataSpaceDefinitions() { ThrowIfIncorrectUpdaterVersion(); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); // // Write out data space definitions if( 0 < _dataSpaceDefinitions.Count ) { StorageInfo dataSpaceDefinitionsStorage = new StorageInfo(dataSpaceStorage, DataSpaceDefinitionsStorageName); dataSpaceDefinitionsStorage.Create(); foreach( string name in _dataSpaceDefinitions.Keys ) { StreamInfo singleDefinitionInfo = new StreamInfo(dataSpaceDefinitionsStorage,name); using(Stream singleDefinition = singleDefinitionInfo.GetStream()) { using(BinaryWriter definitionWriter =new BinaryWriter( singleDefinition, System.Text.Encoding.Unicode )) { DataSpaceDefinition definition = (DataSpaceDefinition) _dataSpaceDefinitions[name]; int headerSize = KnownBytesInDataSpaceDefinitionHeader; if (definition.ExtraData != null) { checked { headerSize += definition.ExtraData.Length; } } definitionWriter.Write( headerSize ); definitionWriter.Write( definition.TransformStack.Count ); if (definition.ExtraData != null) { definitionWriter.Write(definition.ExtraData); } foreach( object transformLabel in definition.TransformStack) { CU.WriteByteLengthPrefixedDWordPaddedUnicodeString( definitionWriter, (string)transformLabel); } } } } } } ////// Read all transform definitions in one chunk /// // void ReadTransformDefinitions() { ThrowIfIncorrectReaderVersion(); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); StorageInfo transformDefinitionsStorage = new StorageInfo( dataSpaceStorage, TransformDefinitions ); if( transformDefinitionsStorage.Exists ) { // Read transform definitions from file foreach( StorageInfo transformStorage in transformDefinitionsStorage.GetSubStorages() ) { // Read from primary stream StreamInfo transformPrimary = new StreamInfo( transformStorage, TransformPrimaryInfo ); using(Stream transformDefinition = transformPrimary.GetStream(FileMode.Open)) { using(BinaryReader definitionReader = new BinaryReader( transformDefinition, System.Text.Encoding.Unicode )) { int headerLength = definitionReader.ReadInt32(); // We don't actually do anything with HeaderLen at the moment int transformType = definitionReader.ReadInt32(); if (headerLength < 0) throw new FileFormatException(SR.Get(SRID.CorruptedData)); // Create a TransformInstance class using name from file TransformInstance transformInstance = new TransformInstance(transformType, CU.ReadByteLengthPrefixedDWordPaddedUnicodeString( definitionReader ) ); int extraDataSize = checked ((int) (headerLength - transformDefinition.Position)); if (extraDataSize < 0) throw new FileFormatException(SR.Get(SRID.CorruptedData)); if (extraDataSize > 0) { if (extraDataSize > AllowedExtraDataMaximumSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); // Preserve the fields we don't know about. byte[] extraData = definitionReader.ReadBytes(extraDataSize); if (extraData.Length != extraDataSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); transformInstance.ExtraData = extraData; } if( transformDefinition.Length > transformDefinition.Position ) { // We have additional data in the primary instance data stream int instanceDataSize = checked ((int)(transformDefinition.Length - transformDefinition.Position)); byte[] instanceData = new byte[instanceDataSize]; PackagingUtilities.ReliableRead(transformDefinition, instanceData, 0, instanceDataSize); //build memory stream on the byte[] , and allow writes only if // FileAccess is Write or ReadWrite MemoryStream instanceDataStream; if (_associatedStorage.OpenAccess == FileAccess.Read) { // NOTE: Building MemoryStream directly on top of // instanceData byte array because we want it to be // NOT resizable and NOT writable. instanceDataStream = new MemoryStream(instanceData, false /* Not writable */); } else { // Copy additional data into a memory stream // NOTE: Not building MemoryStream directly on top of // instanceData byte array because we want it to be // resizable. instanceDataStream = new MemoryStream(); instanceDataStream.Write( instanceData, 0, instanceDataSize ); } instanceDataStream.Seek( 0, SeekOrigin.Begin ); // Dirty state should be tracked after the original data is read in from the disk into the memory stream transformInstance.transformPrimaryStream = new DirtyStateTrackingStream(instanceDataStream); } transformInstance.transformStorage = transformStorage; SetTransformDefinition( transformStorage.Name, transformInstance ); } } } } } ////// Write out all transform definitions all at once /// /// // void WriteTransformDefinitions() { ThrowIfIncorrectUpdaterVersion(); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); StorageInfo transformDefinitionsStorage = new StorageInfo( dataSpaceStorage, TransformDefinitions ); // if( 0 < _transformDefinitions.Count ) { foreach( string transformDef in _transformDefinitions.Keys ) { // 'transformDef' is the normalized label. We need to dig a // bit to retrieve the original transform label. string transformLabel = null; TransformInstance transformInstance = GetTransformInstanceOf( transformDef ); Debug.Assert( transformInstance != null, "A transform instance should be available if its name is in the transformDefinitions hashtable"); if( transformInstance.transformEnvironment != null ) { // We have a transform environment object - it has the transform label. transformLabel = transformInstance.transformEnvironment.TransformLabel; } else { // We don't have a transform environment object - we'll need to do a // more expensive reverse-lookup with the name manager. transformLabel = transformDef; } // Now use transformLabel to create the storage. StorageInfo singleTransformStorage = new StorageInfo( transformDefinitionsStorage, transformLabel ); StreamInfo transformPrimaryInfo = new StreamInfo( singleTransformStorage, TransformPrimaryInfo ); using(Stream transformPrimary = transformPrimaryInfo.GetStream()) { using(BinaryWriter transformWriter = new BinaryWriter( transformPrimary, System.Text.Encoding.Unicode )) { // Header length size = Known (the number itself + identifier) + // to be calculated (length of type name) int headerLength = checked (KnownBytesInTransformDefinitionHeader + CU.WriteByteLengthPrefixedDWordPaddedUnicodeString( null, transformInstance.typeName)); if (transformInstance.ExtraData != null) { Debug.Assert(transformInstance.ExtraData.Length > 0); checked { headerLength += transformInstance.ExtraData.Length; } } transformWriter.Write(headerLength); transformWriter.Write((int)TransformIdentifierTypes_PredefinedTransformName); CU.WriteByteLengthPrefixedDWordPaddedUnicodeString( transformWriter, transformInstance.typeName); // Write out the preserved unknown data if there are some if (transformInstance.ExtraData != null) { transformWriter.Write(transformInstance.ExtraData); } if( null != transformInstance.transformPrimaryStream ) { byte[] memoryBuffer = ((MemoryStream) ((DirtyStateTrackingStream) transformInstance.transformPrimaryStream).BaseStream).GetBuffer(); transformPrimary.Write( memoryBuffer, 0, memoryBuffer.Length ); } } } } } else // No transform definitions { if ( transformDefinitionsStorage.Exists) { dataSpaceStorage.Delete(true, TransformDefinitions); //transformDefinitionStorage.Delete(true); } } } ////// Internal method to create an entry in the data space mapping table /// /// Package reference describing the scope of the data space /// Label of the data space definition internal void CreateDataSpaceMapping( CompoundFileReference containerReference, string label ) { Debug.Assert( DataSpaceIsDefined( label ), "Internal method illegally defining a data space reference on a data space that has not yet been defined"); _dataSpaceMap[containerReference] = label; _dirtyFlag = true; } ////// Internal method to create the actual transform stack of streams which /// will handle the encoding and decoding /// /// Package reference for the data space stream /// Stream with raw bytes on disk ///Stream with clear text internal Stream CreateDataSpaceStream( CompoundFileStreamReference containerReference , Stream rawStream ) { Stream outputStream = rawStream; // RightsManagementEncryptionTransform and CompressionTransform do not use transformContext IDictionary transformContext = new Hashtable(); // Transform context object // See which data space we're creating this stream in string dataSpaceLabel = _dataSpaceMap[containerReference] as string; Debug.Assert( null != dataSpaceLabel, "Internal caller has asked to create a data space stream but the reference is not associated with a data space" ); // Retrieve the transform object stack ArrayList transformStack = GetDataSpaceDefinition(dataSpaceLabel).TransformStack; Debug.Assert( null != transformStack, "Internal inconsistency - data space name does not have a transform stack definition" ); // Iterate the initialization process for each transform on the stack foreach( string transformLabel in transformStack ) { // Get information on this layer of the transform stack TransformInstance transformLayer = GetTransformInstanceOf( transformLabel ); Debug.Assert( null != transformLayer, "Data space definition included an undefined transform" ); // If we haven't gotten around to creating the transform object, go do it. if( null == transformLayer.transformReference ) { transformLayer.transformEnvironment = new TransformEnvironment( this, transformLabel ); transformLayer.transformReference = InstantiateDataTransformObject( transformLayer.ClassType, transformLayer.typeName, transformLayer.transformEnvironment ); } Debug.Assert( null != transformLayer.transformReference, "Failed to have a transform instance going in" ); IDataTransform transformObject = transformLayer.transformReference; // If transform is not ready, call initializers to make it ready. if( ! transformObject.IsReady ) { CallTransformInitializers( new TransformInitializationEventArgs( transformObject, dataSpaceLabel, containerReference.FullName, transformLabel) ); if( ! transformObject.IsReady ) // If STILL not ready, nobody could make it "ready". throw new InvalidOperationException( SR.Get(SRID.TransformObjectInitFailed)); } // Everything is setup, get a transformed stream outputStream = transformObject.GetTransformedStream( outputStream, transformContext ); } outputStream = new BufferedStream( outputStream ); // Add buffering layer outputStream = new StreamWithDictionary( outputStream, transformContext ); _transformedStreams.Add( outputStream ); // Remember this for later use return outputStream; } ////// When naming a transform object, the string being passed in can be /// interpreted in one of several ways. This enumerated type is used /// to specify the semantics of the identification string. /// /// The transform identification string is key into a table of /// well-known transform definitions. /// internal const int TransformIdentifierTypes_PredefinedTransformName = 1; #region Version Methods ////// Read the version information that specifies the minimum versions of the /// DataSpaceManager software that can read, write, or update the data space /// information in this file. /// /// ////// If the format version information in the stream is corrupt. /// private void ReadDataSpaceVersionInformation(StorageInfo dataSpaceStorage) { if (_fileFormatVersion == null) { if (dataSpaceStorage.StreamExists( DataSpaceVersionName )) { StreamInfo versionStreamInfo = dataSpaceStorage.GetStreamInfo( DataSpaceVersionName ); using (Stream versionStream = versionStreamInfo.GetStream(FileMode.Open)) { _fileFormatVersion = FormatVersion.LoadFromStream(versionStream); // Transform Identifier: we preserve casing, but do case-insensitive comparison //Case-insensitive comparison. As per recommendations, we convert both strings //to Upper case and then compare with StringComparison.Ordinal if (!((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(_fileFormatVersion.FeatureIdentifier, DataSpaceVersionIdentifier)) { throw new FileFormatException( SR.Get(SRID.InvalidTransformFeatureName, _fileFormatVersion.FeatureIdentifier, DataSpaceVersionIdentifier)); } // If we ever write this version number out again, we will want to record // the fact that it was done by the current version of the Dataspace software. _fileFormatVersion.WriterVersion = DataSpaceCurrentWriterVersion; } } } } ////// If the DataSpace version information was not present in the file, we initialize it with the /// values for the current DataSpace software. /// If the information was present we should have already read it as a part of the ReadDataSpaceMap. /// private void EnsureDataSpaceVersionInformation() { if (_fileFormatVersion == null) { _fileFormatVersion = new FormatVersion( DataSpaceVersionIdentifier, DataSpaceCurrentWriterVersion, DataSpaceCurrentReaderVersion, DataSpaceCurrentUpdaterVersion ); } } ////// Verify that the current version of this class can read the DataSpace information in /// this file. /// ////// If the current version of this class cannot read the DataSpace information in this file. /// private void ThrowIfIncorrectReaderVersion() { EnsureDataSpaceVersionInformation(); if (!_fileFormatVersion.IsReadableBy(DataSpaceCurrentReaderVersion)) { throw new FileFormatException( SR.Get( SRID.ReaderVersionError, _fileFormatVersion.ReaderVersion, DataSpaceCurrentReaderVersion ) ); } } ////// Verify that the current version of this class can update the DataSpace information in /// this file. /// ////// If the current version of this class cannot update the DataSpace information in this file, /// or if the header information in the stream is corrupt. /// private void ThrowIfIncorrectUpdaterVersion() { EnsureDataSpaceVersionInformation(); if (!_fileFormatVersion.IsUpdatableBy(DataSpaceCurrentUpdaterVersion)) { throw new FileFormatException( SR.Get( SRID.UpdaterVersionError, _fileFormatVersion.UpdaterVersion, DataSpaceCurrentUpdaterVersion ) ); } } #endregion Version Methods } ////// Interface to be implemented by all data transform objects /// internal interface IDataTransform { ////// Whether this transform is ready to perform. If false, it needs /// further initialization. /// bool IsReady { get; } ////// Whether this transform is in a stable state /// bool FixedSettings { get; } ////// Data transform identifier object /// object TransformIdentifier { get; } ////// Given a stream for storing the encoded data, return a stream for /// manipulating the "cleartext" data. /// Stream GetTransformedStream( Stream encodedDataStream, IDictionary transformContext ); } ////// Internal class for passing arguments into event handlers /// internal class TransformInitializationEventArgs : EventArgs { IDataTransform dataInstance; string dataSpaceLabel; string streamPath; string transformLabel; internal TransformInitializationEventArgs( IDataTransform instance, string dataSpaceInstanceLabel, string transformedStreamPath, string transformInstanceLabel ) { dataInstance = instance; dataSpaceLabel = dataSpaceInstanceLabel; streamPath = transformedStreamPath; transformLabel = transformInstanceLabel; } ////// Reference to the data object that requires initialization /// internal IDataTransform DataTransform { get { return dataInstance; } } ////// Label for the data space whose initialization this is a part of /// internal string DataSpaceLabel { get { return dataSpaceLabel; } } ////// The path to the stream whose use initiated this process /// internal string Path { get { return streamPath; } } ////// Label for the transform object instance being initialized /// internal string TransformInstanceLabel { get { return transformLabel; } } } ////// An instance of this class is given to each transform object as a /// means for the transform object to interact with the environment /// provided by the data space manager. It is not mandatory for a /// transform object to keep a reference on the given TransformEnvironment /// object it may choose to discard it if there is no need to interact /// with the transform environment. /// internal class TransformEnvironment { DataSpaceManager transformHost; string transformLabel; ////// This object is only created internally by the data space manager. /// /// The data space manager who created this class /// Text label for this transform instance internal TransformEnvironment( DataSpaceManager host, string instanceLabel ) { transformHost = host; transformLabel = instanceLabel; } ////// Whether this transform demands that all instance data of other /// transform above it in the transform stack be transformed too /// internal bool RequireOtherInstanceData { get { return false; } set { transformHost.CheckDisposedStatus(); throw new NotSupportedException( SR.Get(SRID.NYIDefault)); } } ////// Whether this transform demands that its own instance data be /// free from other data transformation /// internal bool RequireInstanceDataUnaltered { get { return false; } set { transformHost.CheckDisposedStatus(); throw new NotSupportedException( SR.Get(SRID.NYIDefault)); } } ////// Whether this transform requests that its own instance data be /// processed by other transforms below it in the stack /// internal bool DefaultInstanceDataTransform { get { return false; } set { transformHost.CheckDisposedStatus(); throw new NotSupportedException( SR.Get(SRID.NYIDefault)); } } ////// In the transform definition hashtable, we only have the normalized /// label as the key. By digging our way to this property via the hashtable /// value, we can recover the original label. /// internal string TransformLabel { get { return transformLabel; } } ////// Returns the stream specific to this instance of the transform /// object, in which it can store any instance data it needs /// ///System.IO.Stream object for storing instance data internal Stream GetPrimaryInstanceData() { transformHost.CheckDisposedStatus(); return transformHost.GetPrimaryInstanceStreamOf( transformLabel ); } ////// When the primary instance data stream is insufficient, this returns /// a storage in which to store multiple streams that comprise the instance /// data that this transform object wants to store. /// ///StorageInfo pointing to the storage that this transform is free to use internal StorageInfo GetInstanceDataStorage() { transformHost.CheckDisposedStatus(); StorageInfo storageInfo = transformHost.GetInstanceDataStorageOf( transformLabel ); if (! storageInfo.Exists) { storageInfo.Create(); } return storageInfo; } } } // 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: // The object for manipulating data spaces within the WPP Package. // // History: // 06/04/2002: RogerCh: Initial creation of class & method stubs. // 06/17/2002: RogerCh: Initial implementation. // 05/20/2003: RogerCh: Ported to WCP tree. // 05/28/2003: RogerCh: Added long name support // //----------------------------------------------------------------------------- using System; using System.Collections; using System.Diagnostics; // For Debug.Assert using System.Globalization; using System.IO; using System.Reflection; // For finding transform objects & their constructor using System.Security.Permissions; // For StrongNameIdentityPermission using System.Collections.Generic; using System.Windows; // ExceptionStringTable using MS.Internal.IO.Packaging; using MS.Internal.IO.Packaging.CompoundFile; using CU = MS.Internal.IO.Packaging.CompoundFile.ContainerUtilities; namespace System.IO.Packaging { ////// This class is used to manipulate the data spaces within a specific instance /// of the Avalon container. This is how data transform modules are plugged /// into the container to enable features like data compression and data /// encryption. /// internal class DataSpaceManager { /***********************************************************************/ // Constants // The header bytes that this version understands and supports const int KnownBytesInMapTableHeader = 8; // Two Int32s == 8 bytes const int KnownBytesInDataSpaceDefinitionHeader = 8; const int KnownBytesInTransformDefinitionHeader = 8; const int AllowedExtraDataMaximumSize = 8192; // 8K // Names for streams and storages within the container const string DataSpaceStorageName = "\x0006DataSpaces"; const string DataSpaceVersionName = "Version"; const string DataSpaceMapTableName= "DataSpaceMap"; const string DataSpaceDefinitionsStorageName = "DataSpaceInfo"; const string TransformDefinitions = "TransformInfo"; const string TransformPrimaryInfo = "\x0006Primary"; // The string used in FormatVersion private static readonly string DataSpaceVersionIdentifier = "Microsoft.Container.DataSpaces"; // Version Writer - 1.0, Reader - 1.0, Updater - 1.0 private static readonly VersionPair DataSpaceCurrentWriterVersion = new VersionPair(1 /*major*/, 0 /*minor*/); private static readonly VersionPair DataSpaceCurrentReaderVersion = new VersionPair(1 /*major*/, 0 /*minor*/); private static readonly VersionPair DataSpaceCurrentUpdaterVersion = new VersionPair(1 /*major*/, 0 /*minor*/); // The version information we read from the file private FormatVersion _fileFormatVersion; private bool _dirtyFlag; /***********************************************************************/ // Data space manager instance values ////// There is only one data space manager per container instance. This /// points back to "our" reference. /// StorageRoot _associatedStorage; ////// Maps container references to data spaces /// /// Keys into this list are CompoundFileReference instances, each /// representing a subset of the container that is encoded with a /// particular data space. /// /// Values are strings, which are data space labels and can be used /// as keys into _dataSpaceDefinitions for more details /// SortedList _dataSpaceMap; ////// Extra data in the data space mapping table header is preserved /// in this byte array. /// byte[] _mapTableHeaderPreservation; ////// Maps a data space name to a string array of transform names. /// /// Keys into this hash table are strings, each a unique label for /// a data space. /// /// Values from this hash table are ArrayLists, each an array of /// strings. Each string is a data space label. This transform /// stack is stored in bottom-up order. The first transform listed /// is the first to get the raw bytes from disk. /// Hashtable _dataSpaceDefinitions; ////// Maps a transform name to an instance of transform handle class /// /// Keys into this hash table are strings, each a unique label for /// a transform object instance. /// /// Values from this hash table are references to the TransformInstance /// class defined below, each of which contains information for a /// particular transform instance. /// Hashtable _transformDefinitions; ////// When shutting down, we need to flush each open transformed stream in /// order to ensure that all encoding data has been propagated through /// the transform stack before we shut things down. Otherwise we may leave /// data in a state where it could not be written out because parts of the /// transform stack has already been disposed. /// ArrayList _transformedStreams; ////// Table of "well-known" -- that is, "built-in" -- transforms. The keys are /// the TransformClassName identifier strings for the well-known transforms, /// such as encryption and compression. The values are the assembly-qualified /// .NET class names of the classes that implement the transforms. /// static readonly Hashtable _transformLookupTable; /***********************************************************************/ // Private class for tracking individual transform instances private class TransformInstance { // When we only know the CLR type name internal TransformInstance( int classType, string name ) : this(classType, name, null, null, null, null ) {;} // When we also have an actual object in memory and its associated // environment object internal TransformInstance( int classType, string name, IDataTransform instance, TransformEnvironment environment ) : this(classType, name, instance, environment, null, null ) {;} // When we know everything to put into a TransformInstance. internal TransformInstance( int classType, string name, IDataTransform instance, TransformEnvironment environment, Stream primaryStream, StorageInfo storage ) { typeName = name; transformReference = instance; transformEnvironment = environment; transformPrimaryStream = primaryStream; transformStorage = storage; _classType = classType; } internal byte[] ExtraData { get { return _extraData; } set { _extraData = value; } } internal int ClassType { get { return _classType; } } ////// This is the CLR name used to define this transform. Keep in /// mind that this is not necessarily what we retrieve when we /// call Type.FullName or Type.AssemblyQualifiedName. This is /// the name that needs to be persisted in the file because it is /// what the caller told us to use to find the type. /// internal string typeName; ////// If we have actually created the transform object, we keep /// a reference to it here. This may be null if we haven't /// had a need to create the transform object. /// internal IDataTransform transformReference; ////// The instance of TransformEnvironment that we created and /// handed off to the transform object to tell us about things. /// This can also be null, but only when transformReference is /// null. It is not valid for only one of these two to be null, /// either they're both null or they're both non-null. /// internal TransformEnvironment transformEnvironment; ////// The stream that is the primary data storage for this instance /// of the transform object. /// internal Stream transformPrimaryStream; ////// The storage that is available if the transform object requires /// more than the primary stream /// internal StorageInfo transformStorage; private int _classType; private byte[] _extraData; } private class DirtyStateTrackingStream: Stream { //////////////////////////////////// // Stream section ///////////////////////////////// public override bool CanRead { get { return (_baseStream != null && _baseStream.CanRead); } } public override bool CanSeek { get { return (_baseStream != null && _baseStream.CanSeek); } } public override bool CanWrite { get { return (_baseStream != null && _baseStream.CanWrite); } } public override long Length { get { CheckDisposed(); return _baseStream.Length; } } public override long Position { get { CheckDisposed(); return _baseStream.Position; } set { CheckDisposed(); _baseStream.Position = value; } } public override void SetLength(long newLength) { CheckDisposed(); if (newLength != _baseStream.Length) { _dirty = true; } _baseStream.SetLength(newLength); } public override long Seek(long offset, SeekOrigin origin) { CheckDisposed(); return _baseStream.Seek(offset, origin); } public override int Read(byte[] buffer, int offset, int count) { CheckDisposed(); return _baseStream.Read(buffer, offset, count); } public override void Write(byte[] buffer, int offset, int count) { CheckDisposed(); _baseStream.Write(buffer, offset, count); _dirty = true; } public override void Flush() { CheckDisposed(); _baseStream.Flush(); } ///////////////////////////// // Internal Constructor ///////////////////////////// internal DirtyStateTrackingStream(Stream baseStream) { Debug.Assert(baseStream != null); _baseStream = baseStream; } internal bool DirtyFlag { get { return (_baseStream != null && _dirty); } } internal Stream BaseStream { get { return _baseStream; } } //----------------------------------------------------- // // Protected Methods // //----------------------------------------------------- ////// Dispose(bool) /// /// ///We implement this because we want a consistent experience (essentially Flush our data) if the user chooses to /// call Dispose() instead of Close(). protected override void Dispose(bool disposing) { try { if (disposing) { if (_baseStream != null) _baseStream.Close(); } } finally { _baseStream = null; base.Dispose(disposing); } } ///////////////////////////// // Private Methods ///////////////////////////// private void CheckDisposed() { if (_baseStream == null) { throw new ObjectDisposedException(null, SR.Get(SRID.StreamObjectDisposed)); } } private bool _dirty; private Stream _baseStream; } private struct DataSpaceDefinition { ArrayList _transformStack; Byte[] _extraData; internal DataSpaceDefinition(ArrayList transformStack, Byte[] extraData) { _transformStack = transformStack; _extraData = extraData; } internal ArrayList TransformStack { get { return _transformStack; } } internal Byte[] ExtraData { get { return _extraData; } } } /***********************************************************************/ // Constructors ////// Static constructor that initializes the transformLookupTable (which see). /// static DataSpaceManager() { // Transform Identifier: we preserve casing, but do case-insensitive comparison _transformLookupTable = new Hashtable(CU.StringCaseInsensitiveComparer); _transformLookupTable[RightsManagementEncryptionTransform.ClassTransformIdentifier] = "System.IO.Packaging.RightsManagementEncryptionTransform"; _transformLookupTable[CompressionTransform.ClassTransformIdentifier] = "System.IO.Packaging.CompressionTransform"; } ////// Internally visible constructor /// /// The container instance we're associated with internal DataSpaceManager( StorageRoot containerInstance ) { _associatedStorage = containerInstance; // Storage under which all data space information is stored. StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); // Initialize internal data structures. // _dataSpaceMap = new SortedList(); _mapTableHeaderPreservation = new byte[0]; _dataSpaceDefinitions = new Hashtable(CU.StringCaseInsensitiveComparer); _transformDefinitions = new Hashtable(CU.StringCaseInsensitiveComparer); _transformedStreams = new ArrayList(); // Check to see if we have any data space information to read if (dataSpaceStorage.Exists) { // Read any existing data space mapping information from the container // ReadDataSpaceMap(); ReadDataSpaceDefinitions(); ReadTransformDefinitions(); } return; } ////// Returns the number of data spaces defined in this manager object /// internal int Count { get { CheckDisposedStatus(); return _dataSpaceMap.Count; } } private bool DirtyFlag { get { if (_dirtyFlag) // It is already dirty don't need to check further return true; foreach( string transformDef in _transformDefinitions.Keys ) { TransformInstance transformInstance = GetTransformInstanceOf( transformDef ); if (((DirtyStateTrackingStream) transformInstance.transformPrimaryStream).DirtyFlag) { return true; } } return false; } } ////// Clean up the data space information and flush all data to the container. /// public void Dispose() { CheckDisposedStatus(); // Flush any outstanding data in the transformed streams foreach( StreamWithDictionary dataStream in _transformedStreams ) { if (!dataStream.Disposed) dataStream.Flush(); } _transformedStreams.Clear(); // Now that all data have been flushed, shut down the transform // objects. foreach( object o in _transformDefinitions.Values ) { IDataTransform idt = ((TransformInstance)o).transformReference; if( null != idt && null != (idt as IDisposable)) { ((IDisposable)idt).Dispose(); } } // Now write the tables out. // if (FileAccess.Read != _associatedStorage.OpenAccess && DirtyFlag) { WriteDataSpaceMap(); WriteDataSpaceDefinitions(); WriteTransformDefinitions(); } _dataSpaceMap = null; _dataSpaceDefinitions = null; _transformDefinitions = null; return; } ////// Removes the container reference from the dataspace map. This is called when the container/substorage is getting deleted from the root storage. [DeleteSubStorage in StorageInfo] /// internal void RemoveContainerFromDataSpaceMap(CompoundFileReference target) { CheckDisposedStatus(); if (_dataSpaceMap.Contains(target)) { _dataSpaceMap.Remove(target); _dirtyFlag = true; } } // Check to see if the dispose method has been called. If so, throw an // ObjectDisposedException. internal void CheckDisposedStatus() { // First check root _associatedStorage.CheckRootDisposedStatus(); // Check if we've been disposed if( null == _dataSpaceMap ) { Debug.Assert( null == _dataSpaceDefinitions, "Having a null data space map and a non-null data space definitions map is an inconsistent state" ); Debug.Assert( null == _transformDefinitions, "Having a null data space map and a non-null transform definition map is an inconsistent state" ); throw new ObjectDisposedException(null, SR.Get(SRID.DataSpaceManagerDisposed)); } } ////// Define a data space with the given stack of transform objects and /// labeled with the given name. The transform stack is interpreted in /// bottom-up order. (Clear-text transform is last.) /// /// Transform stack /// New data space label internal void DefineDataSpace( string[] transformStack, string newDataSpaceLabel ) { CheckDisposedStatus(); // Data space must have at least one transform if( null == transformStack || 0 == transformStack.Length ) throw new ArgumentException( SR.Get(SRID.TransformStackValid)); // Given label must be a non-empty string CU.CheckStringAgainstNullAndEmpty(newDataSpaceLabel, "newDataSpaceLabel"); // Given label must not be a reserved string CU.CheckStringAgainstReservedName(newDataSpaceLabel, "newDataSpaceLabel"); // Given label must not already be in use if( DataSpaceIsDefined( newDataSpaceLabel ) ) throw new ArgumentException( SR.Get(SRID.DataSpaceLabelInUse)); // Given transform array must include labels that have already been defined foreach( string transformLabel in transformStack ) { CU.CheckStringAgainstNullAndEmpty( transformLabel, "Transform label" ); if( !TransformLabelIsDefined( transformLabel ) ) throw new ArgumentException( SR.Get(SRID.TransformLabelUndefined)); } // Passes all inspection, data space definition successful. SetDataSpaceDefinition( newDataSpaceLabel, new DataSpaceDefinition(new ArrayList(transformStack), null)); _dirtyFlag = true; return; } ////// Internal shortcut to check if data space is defined. When we start /// doing on-demand reads of data space definitions, the "demand" could /// be triggered by this. /// /// Label to check ///True if label is in use internal bool DataSpaceIsDefined( string dataSpaceLabel ) { CU.CheckStringAgainstNullAndEmpty(dataSpaceLabel, "dataSpaceLabel"); return _dataSpaceDefinitions.Contains( dataSpaceLabel ); } ////// Central place to set a data space definition, centralizing the /// call to the name manager. /// private void SetDataSpaceDefinition( string dataSpaceLabel, DataSpaceDefinition definition ) { _dataSpaceDefinitions[ dataSpaceLabel ] = definition; } ////// Central place to get a data space definition, centralizing the /// call to the name manager. /// private DataSpaceDefinition GetDataSpaceDefinition( string dataSpaceLabel ) { return ((DataSpaceDefinition) _dataSpaceDefinitions[dataSpaceLabel]); } ////// Internal method to retrieve the data space label corresponding to /// a container reference. Returns null if no data space is associated. /// /// CompoundFileReference whose data space label is to be retrieved ///Data space label internal string DataSpaceOf( CompoundFileReference target ) { // Can't simply return _dataSpaceMap[target] because I need to cast it // into a string, and if it's null the cast `blows up. if( _dataSpaceMap.Contains(target) ) { return (string)_dataSpaceMap[target]; } else { return null; } } ////// This method returns all the transforms that are applied to a particular stream as an /// List of IDataTransform objects. /// /// StreamInfo for the stream whose transforms are requested ///A List of IDataTransform objects that are applied to the stream represented by streamInfo internal ListGetTransformsForStreamInfo(StreamInfo streamInfo) { string dataSpaces = this.DataSpaceOf(streamInfo.StreamReference); if (dataSpaces == null) // No datas pace is associated with the stream { return new List (0); // return an empty list } ArrayList transformsList = this.GetDataSpaceDefinition(dataSpaces).TransformStack; List dataTransforms = new List (transformsList.Count); for (int i = 0; i < transformsList.Count; i++) { dataTransforms.Add(this.GetTransformFromName(transformsList[i] as string)); } return dataTransforms; } /// /// Define a data space with the given stack of transform objects and /// labeled with an auto-generated name /// /// Transform stack ///The auto-generated label for this data space internal string DefineDataSpace( string[] transformStack ) { CheckDisposedStatus(); Int64 timeSeed = DateTime.Now.ToFileTime(); string generatedName = timeSeed.ToString(CultureInfo.InvariantCulture); // If there is a name collision, just keep incrementing the number while( DataSpaceIsDefined( generatedName ) ) { timeSeed++; generatedName = timeSeed.ToString(CultureInfo.InvariantCulture); } // Submit the definition DefineDataSpace( transformStack, generatedName ); return generatedName; } ////// Tranform object is created. We are no longer using reflection to do this. We are supporting limited data transforms. /// private IDataTransform InstantiateDataTransformObject(int transformClassType, string transformClassName, TransformEnvironment transformEnvironment ) { object transformInstance = null; if (transformClassType != (int) TransformIdentifierTypes_PredefinedTransformName) throw new NotSupportedException(SR.Get(SRID.TransformTypeUnsupported)); // Transform Identifier: we preserve casing, but do case-insensitive comparison if (((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(transformClassName, RightsManagementEncryptionTransform.ClassTransformIdentifier)) { transformInstance = new RightsManagementEncryptionTransform( transformEnvironment); } else if (((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(transformClassName, CompressionTransform.ClassTransformIdentifier)) { transformInstance = new CompressionTransform( transformEnvironment ); } else { //this transform class is not supported. Need to change this to appropriate error. throw new ArgumentException( SR.Get(SRID.TransformLabelUndefined)); } if (null != transformInstance) { if( !( transformInstance is IDataTransform ) ) throw new ArgumentException( SR.Get(SRID.TransformObjectImplementIDataTransform)); return (IDataTransform)transformInstance; } return null; } ////// Private method to check if a transform label is defined. When we /// start reading transform defintions on-demand, we would probably do it /// here as necessary. /// /// Transform label to check ///True if label is defined in hash table internal bool TransformLabelIsDefined( string transformLabel ) { // return _transformDefinitions.Contains( transformLabel ); } ////// Central place to set a transform definition, centralizing the /// call to the name manager. /// private void SetTransformDefinition( string transformLabel, TransformInstance definition ) { _transformDefinitions[ transformLabel ] = definition; } ////// Private method to get the TransformInstance class representing /// a transform instance. /// private TransformInstance GetTransformInstanceOf( string transformLabel ) { Debug.Assert( TransformLabelIsDefined( transformLabel ), "Data space manager caller failed to verify transform exists before retrieving instance" ); // return _transformDefinitions[ transformLabel ] as TransformInstance; } ////// Internal method to get a MemoryStream whose contents will be /// stored in the "\x0006Primary" data stream after our type identification /// information /// /// Transform Label ///Memory stream object for transform instance primary stream internal Stream GetPrimaryInstanceStreamOf( string transformLabel ) { TransformInstance targetInstance = GetTransformInstanceOf( transformLabel ); if( null == targetInstance.transformPrimaryStream ) { //build memory stream on the byte[0] , and allow writes only if // FileAccess is Write or ReadWrite if (_associatedStorage.OpenAccess == FileAccess.Read) { targetInstance.transformPrimaryStream = new DirtyStateTrackingStream (new MemoryStream (new byte[0], false /* Not writable */)); } else { targetInstance.transformPrimaryStream = new DirtyStateTrackingStream (new MemoryStream()); } } return targetInstance.transformPrimaryStream; } ////// Internal method to get the StorageInfo where the transform instance /// data is stored. This StorageInfo may not yet exist! /// /// Transform Label ///StorageInfo pointing to transform instance data storage internal StorageInfo GetInstanceDataStorageOf( string transformLabel ) { TransformInstance targetInstance = GetTransformInstanceOf( transformLabel ); if( null == targetInstance.transformStorage ) { //string name = DataSpaceStorageName + '\\' + TransformDefinitions + '\\' + transformLabel; //targetInstance.transformStorage = new StorageInfo(_associatedStorage,name); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); if (!dataSpaceStorage.Exists ) { dataSpaceStorage.Create(); } StorageInfo transformDefinition = new StorageInfo( dataSpaceStorage, TransformDefinitions ); if (!transformDefinition.Exists) { transformDefinition.Create(); } targetInstance.transformStorage = new StorageInfo(transformDefinition,transformLabel); } return targetInstance.transformStorage; } ////// Internal method to get the transform object corresponding to the specified /// transform instance label. The transform object is created if it does not exist. /// /// /// String that identifies the transform instance. /// ////// An IDataTransform interface pointer to the transform object identified by /// internal IDataTransform GetTransformFromName(string transformLabel) { TransformInstance transformInstance = _transformDefinitions[transformLabel] as TransformInstance; if (transformInstance == null) { // // There is no transform instance with the specified name. // return null; } IDataTransform transformObject = transformInstance.transformReference; if (transformObject == null) { // // There is a transform instance with the specified name, but its transform // object has not yet been created. Create it now. This code is modeled on the // code in DefineTransform. // TransformEnvironment transformEnvironment = new TransformEnvironment(this, transformLabel); // Create the transform object. transformObject = InstantiateDataTransformObject( transformInstance.ClassType, transformInstance.typeName, transformEnvironment); transformInstance.transformReference = transformObject; } return transformObject; } ///, or null if there is no such transform. /// /// Define a data transform object with the given object identification and /// labeled with the given name. /// /// Transform identification string /// Label to use for new transform internal void DefineTransform(string transformClassName, string newTransformLabel ) { CheckDisposedStatus(); // Check to see if transform name is obviously invalid CU.CheckStringAgainstNullAndEmpty( transformClassName, "Transform identifier name" ); // Check to see if transform name is valid CU.CheckStringAgainstNullAndEmpty( newTransformLabel, "Transform label" ); // Given transform name must not be a reserved string CU.CheckStringAgainstReservedName( newTransformLabel, "Transform label" ); // Can't re-use an existing transform name if( TransformLabelIsDefined( newTransformLabel ) ) throw new ArgumentException( SR.Get(SRID.TransformLabelInUse)); // Create class the transform object will use to communicate to us TransformEnvironment transformEnvironment = new TransformEnvironment( this, newTransformLabel ); // Create a TransformInstance object to represent this transform instance. TransformInstance newTransform = new TransformInstance( TransformIdentifierTypes_PredefinedTransformName, transformClassName, null, transformEnvironment ); SetTransformDefinition( newTransformLabel, newTransform ); // Create the transform object IDataTransform transformObject = InstantiateDataTransformObject( TransformIdentifierTypes_PredefinedTransformName, transformClassName, transformEnvironment ); newTransform.transformReference = transformObject; // If transform is not ready out-of-the-box, do an initialization run. // Note: Transform is not required to be "ready" after this. This is // done for those transforms that need initialization work up-front. if( ! transformObject.IsReady ) { CallTransformInitializers( new TransformInitializationEventArgs( transformObject, null, null, newTransformLabel) ); } _dirtyFlag = true; return; } ////// Define a data transform object with the given object identification and /// labeled with an auto-generated name. /// /// Transform identification string ///The auto-generated label for this transform internal string DefineTransform( string transformClassName ) { CheckDisposedStatus(); Int64 timeSeed = DateTime.Now.ToFileTime(); string generatedName = timeSeed.ToString(CultureInfo.InvariantCulture); // If there is a name collision, just keep incrementing the number while( TransformLabelIsDefined( generatedName ) ) { timeSeed++; generatedName = timeSeed.ToString(CultureInfo.InvariantCulture); } // Submit the definition DefineTransform( transformClassName, generatedName ); return generatedName; } //+---------------------------------------------------------------------- // Transform initialization event/delegate/etc. ////// Delegate method for initializing transforms /// internal delegate void TransformInitializeEventHandler( object sender, TransformInitializationEventArgs e ); ////// Transform initialization event /// internal event TransformInitializeEventHandler OnTransformInitialization; ////// Internal shortcut to kick off all the initializers /// /// Arguments for the initializers internal void CallTransformInitializers( TransformInitializationEventArgs initArguments ) { if( null != OnTransformInitialization ) OnTransformInitialization( this, initArguments ); } ////// Reads a data space map from the associated container, if such a thing /// is written to the file. /// void ReadDataSpaceMap() { // See if there's even a data spaces storage StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); StreamInfo dataSpaceMapStreamInfo = new StreamInfo( dataSpaceStorage, DataSpaceMapTableName ); if( dataSpaceStorage.StreamExists(DataSpaceMapTableName) ) { // There is an existing data space mapping table to read. // Read the versioning information ReadDataSpaceVersionInformation(dataSpaceStorage); // Check if its the correct version for reading ThrowIfIncorrectReaderVersion(); // Read the data space mapping table using(Stream dataSpaceMapStream = dataSpaceMapStreamInfo.GetStream(FileMode.Open)) { using(BinaryReader dataSpaceMapReader = new BinaryReader( dataSpaceMapStream, System.Text.Encoding.Unicode )) { int headerLength = dataSpaceMapReader.ReadInt32(); int entryCount = dataSpaceMapReader.ReadInt32(); if (headerLength < KnownBytesInMapTableHeader || entryCount < 0) throw new FileFormatException(SR.Get(SRID.CorruptedData)); int extraDataSize = headerLength - KnownBytesInMapTableHeader; if( 0 < extraDataSize ) { if (extraDataSize > AllowedExtraDataMaximumSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); _mapTableHeaderPreservation = dataSpaceMapReader.ReadBytes(extraDataSize); if (_mapTableHeaderPreservation.Length != extraDataSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); } _dataSpaceMap.Capacity = entryCount; int entryLength; int bytesRead; int totalBytesRead; for( int i = 0; i < entryCount; i++ ) { entryLength = dataSpaceMapReader.ReadInt32(); if (entryLength < 0) throw new FileFormatException(SR.Get(SRID.CorruptedData)); totalBytesRead = 4; // entryLength // Read the container reference entry CompoundFileReference entryRef = CompoundFileReference.Load( dataSpaceMapReader, out bytesRead ); checked { totalBytesRead += bytesRead; } // Read data space string and add to data space mapping table string label = CU.ReadByteLengthPrefixedDWordPaddedUnicodeString(dataSpaceMapReader, out bytesRead); checked { totalBytesRead += bytesRead; } _dataSpaceMap[entryRef] = label; // Verify entryLength against what was actually read: if (entryLength != totalBytesRead) { throw new IOException(SR.Get(SRID.DataSpaceMapEntryInvalid)); } } } } } } ////// Write the data space mapping table to underlying storage. /// void WriteDataSpaceMap() { ThrowIfIncorrectUpdaterVersion(); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); StreamInfo dataSpaceMapStreamInfo = new StreamInfo ( dataSpaceStorage, DataSpaceMapTableName ); if( 0 < _dataSpaceMap.Count ) { // Write versioning information StreamInfo versionStreamInfo = null; if (dataSpaceStorage.StreamExists( DataSpaceVersionName ) ) versionStreamInfo = dataSpaceStorage.GetStreamInfo( DataSpaceVersionName ); else versionStreamInfo = dataSpaceStorage.CreateStream( DataSpaceVersionName ); Stream versionStream = versionStreamInfo.GetStream(); _fileFormatVersion.SaveToStream(versionStream); versionStream.Close(); // Create stream for write, overwrite any existing using(Stream dataSpaceMapStream = dataSpaceMapStreamInfo.GetStream(FileMode.Create)) { using(BinaryWriter dataSpaceMapWriter = new BinaryWriter( dataSpaceMapStream, System.Text.Encoding.Unicode )) { // Write header // header length = our known size + preserved array size dataSpaceMapWriter.Write( checked ((Int32) (KnownBytesInMapTableHeader + _mapTableHeaderPreservation.Length))); // number of entries dataSpaceMapWriter.Write( _dataSpaceMap.Count ); // anything else we've preserved dataSpaceMapWriter.Write( _mapTableHeaderPreservation ); // Loop to write entries foreach( CompoundFileReference o in _dataSpaceMap.Keys ) { // determine the entry length string label = (string)_dataSpaceMap[o]; int entryLength = CompoundFileReference.Save(o, null); checked { entryLength += CU.WriteByteLengthPrefixedDWordPaddedUnicodeString(null, label); } // length of entryLength itself checked { entryLength += 4; } // write out the entry length dataSpaceMapWriter.Write((Int32) entryLength); // Write out reference CompoundFileReference.Save( o, dataSpaceMapWriter); // Write out dataspace label CU.WriteByteLengthPrefixedDWordPaddedUnicodeString( dataSpaceMapWriter, label); } } } } else { // data space map is empty, remove existing stream if there. if ( dataSpaceStorage.StreamExists( DataSpaceMapTableName ) ) dataSpaceStorage.DeleteStream( DataSpaceMapTableName ); } } ////// Read all data space definitions in one chunk. To be replaced /// with on-demand reading mechanism. /// void ReadDataSpaceDefinitions() { ThrowIfIncorrectReaderVersion(); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); StorageInfo dataSpaceDefinitionsStorage = new StorageInfo( dataSpaceStorage, DataSpaceDefinitionsStorageName ); if( dataSpaceDefinitionsStorage.Exists ) { // Fill in the Data Space Definitions hash table foreach( StreamInfo definitionStreamInfo in dataSpaceDefinitionsStorage.GetStreams()) { // Open up the stream for this data space definition using(Stream definitionStream = definitionStreamInfo.GetStream(FileMode.Open)) { using(BinaryReader definitionReader = new BinaryReader( definitionStream, System.Text.Encoding.Unicode )) { // Read data space definition stream int headerLength = definitionReader.ReadInt32(); int transformCount = definitionReader.ReadInt32(); if (headerLength < KnownBytesInDataSpaceDefinitionHeader || transformCount < 0) throw new FileFormatException(SR.Get(SRID.CorruptedData)); ArrayList transformLabels = new ArrayList(transformCount); byte[] extraData = null; int extraDataSize = headerLength - KnownBytesInDataSpaceDefinitionHeader; if (extraDataSize > AllowedExtraDataMaximumSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); if (extraDataSize > 0) { extraData = definitionReader.ReadBytes(extraDataSize); if (extraData.Length != extraDataSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); } // Read the array of strings that make up the transform stack for( int i = 0; i < transformCount; i++ ) { transformLabels.Add( CU.ReadByteLengthPrefixedDWordPaddedUnicodeString( definitionReader ) ); } // Add data space definition to table SetDataSpaceDefinition( definitionStreamInfo.Name, new DataSpaceDefinition(transformLabels, extraData)); } } } } } ////// Write all data space definitions to underlying storage in one chunk. /// /// // void WriteDataSpaceDefinitions() { ThrowIfIncorrectUpdaterVersion(); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); // // Write out data space definitions if( 0 < _dataSpaceDefinitions.Count ) { StorageInfo dataSpaceDefinitionsStorage = new StorageInfo(dataSpaceStorage, DataSpaceDefinitionsStorageName); dataSpaceDefinitionsStorage.Create(); foreach( string name in _dataSpaceDefinitions.Keys ) { StreamInfo singleDefinitionInfo = new StreamInfo(dataSpaceDefinitionsStorage,name); using(Stream singleDefinition = singleDefinitionInfo.GetStream()) { using(BinaryWriter definitionWriter =new BinaryWriter( singleDefinition, System.Text.Encoding.Unicode )) { DataSpaceDefinition definition = (DataSpaceDefinition) _dataSpaceDefinitions[name]; int headerSize = KnownBytesInDataSpaceDefinitionHeader; if (definition.ExtraData != null) { checked { headerSize += definition.ExtraData.Length; } } definitionWriter.Write( headerSize ); definitionWriter.Write( definition.TransformStack.Count ); if (definition.ExtraData != null) { definitionWriter.Write(definition.ExtraData); } foreach( object transformLabel in definition.TransformStack) { CU.WriteByteLengthPrefixedDWordPaddedUnicodeString( definitionWriter, (string)transformLabel); } } } } } } ////// Read all transform definitions in one chunk /// // void ReadTransformDefinitions() { ThrowIfIncorrectReaderVersion(); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); StorageInfo transformDefinitionsStorage = new StorageInfo( dataSpaceStorage, TransformDefinitions ); if( transformDefinitionsStorage.Exists ) { // Read transform definitions from file foreach( StorageInfo transformStorage in transformDefinitionsStorage.GetSubStorages() ) { // Read from primary stream StreamInfo transformPrimary = new StreamInfo( transformStorage, TransformPrimaryInfo ); using(Stream transformDefinition = transformPrimary.GetStream(FileMode.Open)) { using(BinaryReader definitionReader = new BinaryReader( transformDefinition, System.Text.Encoding.Unicode )) { int headerLength = definitionReader.ReadInt32(); // We don't actually do anything with HeaderLen at the moment int transformType = definitionReader.ReadInt32(); if (headerLength < 0) throw new FileFormatException(SR.Get(SRID.CorruptedData)); // Create a TransformInstance class using name from file TransformInstance transformInstance = new TransformInstance(transformType, CU.ReadByteLengthPrefixedDWordPaddedUnicodeString( definitionReader ) ); int extraDataSize = checked ((int) (headerLength - transformDefinition.Position)); if (extraDataSize < 0) throw new FileFormatException(SR.Get(SRID.CorruptedData)); if (extraDataSize > 0) { if (extraDataSize > AllowedExtraDataMaximumSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); // Preserve the fields we don't know about. byte[] extraData = definitionReader.ReadBytes(extraDataSize); if (extraData.Length != extraDataSize) throw new FileFormatException(SR.Get(SRID.CorruptedData)); transformInstance.ExtraData = extraData; } if( transformDefinition.Length > transformDefinition.Position ) { // We have additional data in the primary instance data stream int instanceDataSize = checked ((int)(transformDefinition.Length - transformDefinition.Position)); byte[] instanceData = new byte[instanceDataSize]; PackagingUtilities.ReliableRead(transformDefinition, instanceData, 0, instanceDataSize); //build memory stream on the byte[] , and allow writes only if // FileAccess is Write or ReadWrite MemoryStream instanceDataStream; if (_associatedStorage.OpenAccess == FileAccess.Read) { // NOTE: Building MemoryStream directly on top of // instanceData byte array because we want it to be // NOT resizable and NOT writable. instanceDataStream = new MemoryStream(instanceData, false /* Not writable */); } else { // Copy additional data into a memory stream // NOTE: Not building MemoryStream directly on top of // instanceData byte array because we want it to be // resizable. instanceDataStream = new MemoryStream(); instanceDataStream.Write( instanceData, 0, instanceDataSize ); } instanceDataStream.Seek( 0, SeekOrigin.Begin ); // Dirty state should be tracked after the original data is read in from the disk into the memory stream transformInstance.transformPrimaryStream = new DirtyStateTrackingStream(instanceDataStream); } transformInstance.transformStorage = transformStorage; SetTransformDefinition( transformStorage.Name, transformInstance ); } } } } } ////// Write out all transform definitions all at once /// /// // void WriteTransformDefinitions() { ThrowIfIncorrectUpdaterVersion(); StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName ); StorageInfo transformDefinitionsStorage = new StorageInfo( dataSpaceStorage, TransformDefinitions ); // if( 0 < _transformDefinitions.Count ) { foreach( string transformDef in _transformDefinitions.Keys ) { // 'transformDef' is the normalized label. We need to dig a // bit to retrieve the original transform label. string transformLabel = null; TransformInstance transformInstance = GetTransformInstanceOf( transformDef ); Debug.Assert( transformInstance != null, "A transform instance should be available if its name is in the transformDefinitions hashtable"); if( transformInstance.transformEnvironment != null ) { // We have a transform environment object - it has the transform label. transformLabel = transformInstance.transformEnvironment.TransformLabel; } else { // We don't have a transform environment object - we'll need to do a // more expensive reverse-lookup with the name manager. transformLabel = transformDef; } // Now use transformLabel to create the storage. StorageInfo singleTransformStorage = new StorageInfo( transformDefinitionsStorage, transformLabel ); StreamInfo transformPrimaryInfo = new StreamInfo( singleTransformStorage, TransformPrimaryInfo ); using(Stream transformPrimary = transformPrimaryInfo.GetStream()) { using(BinaryWriter transformWriter = new BinaryWriter( transformPrimary, System.Text.Encoding.Unicode )) { // Header length size = Known (the number itself + identifier) + // to be calculated (length of type name) int headerLength = checked (KnownBytesInTransformDefinitionHeader + CU.WriteByteLengthPrefixedDWordPaddedUnicodeString( null, transformInstance.typeName)); if (transformInstance.ExtraData != null) { Debug.Assert(transformInstance.ExtraData.Length > 0); checked { headerLength += transformInstance.ExtraData.Length; } } transformWriter.Write(headerLength); transformWriter.Write((int)TransformIdentifierTypes_PredefinedTransformName); CU.WriteByteLengthPrefixedDWordPaddedUnicodeString( transformWriter, transformInstance.typeName); // Write out the preserved unknown data if there are some if (transformInstance.ExtraData != null) { transformWriter.Write(transformInstance.ExtraData); } if( null != transformInstance.transformPrimaryStream ) { byte[] memoryBuffer = ((MemoryStream) ((DirtyStateTrackingStream) transformInstance.transformPrimaryStream).BaseStream).GetBuffer(); transformPrimary.Write( memoryBuffer, 0, memoryBuffer.Length ); } } } } } else // No transform definitions { if ( transformDefinitionsStorage.Exists) { dataSpaceStorage.Delete(true, TransformDefinitions); //transformDefinitionStorage.Delete(true); } } } ////// Internal method to create an entry in the data space mapping table /// /// Package reference describing the scope of the data space /// Label of the data space definition internal void CreateDataSpaceMapping( CompoundFileReference containerReference, string label ) { Debug.Assert( DataSpaceIsDefined( label ), "Internal method illegally defining a data space reference on a data space that has not yet been defined"); _dataSpaceMap[containerReference] = label; _dirtyFlag = true; } ////// Internal method to create the actual transform stack of streams which /// will handle the encoding and decoding /// /// Package reference for the data space stream /// Stream with raw bytes on disk ///Stream with clear text internal Stream CreateDataSpaceStream( CompoundFileStreamReference containerReference , Stream rawStream ) { Stream outputStream = rawStream; // RightsManagementEncryptionTransform and CompressionTransform do not use transformContext IDictionary transformContext = new Hashtable(); // Transform context object // See which data space we're creating this stream in string dataSpaceLabel = _dataSpaceMap[containerReference] as string; Debug.Assert( null != dataSpaceLabel, "Internal caller has asked to create a data space stream but the reference is not associated with a data space" ); // Retrieve the transform object stack ArrayList transformStack = GetDataSpaceDefinition(dataSpaceLabel).TransformStack; Debug.Assert( null != transformStack, "Internal inconsistency - data space name does not have a transform stack definition" ); // Iterate the initialization process for each transform on the stack foreach( string transformLabel in transformStack ) { // Get information on this layer of the transform stack TransformInstance transformLayer = GetTransformInstanceOf( transformLabel ); Debug.Assert( null != transformLayer, "Data space definition included an undefined transform" ); // If we haven't gotten around to creating the transform object, go do it. if( null == transformLayer.transformReference ) { transformLayer.transformEnvironment = new TransformEnvironment( this, transformLabel ); transformLayer.transformReference = InstantiateDataTransformObject( transformLayer.ClassType, transformLayer.typeName, transformLayer.transformEnvironment ); } Debug.Assert( null != transformLayer.transformReference, "Failed to have a transform instance going in" ); IDataTransform transformObject = transformLayer.transformReference; // If transform is not ready, call initializers to make it ready. if( ! transformObject.IsReady ) { CallTransformInitializers( new TransformInitializationEventArgs( transformObject, dataSpaceLabel, containerReference.FullName, transformLabel) ); if( ! transformObject.IsReady ) // If STILL not ready, nobody could make it "ready". throw new InvalidOperationException( SR.Get(SRID.TransformObjectInitFailed)); } // Everything is setup, get a transformed stream outputStream = transformObject.GetTransformedStream( outputStream, transformContext ); } outputStream = new BufferedStream( outputStream ); // Add buffering layer outputStream = new StreamWithDictionary( outputStream, transformContext ); _transformedStreams.Add( outputStream ); // Remember this for later use return outputStream; } ////// When naming a transform object, the string being passed in can be /// interpreted in one of several ways. This enumerated type is used /// to specify the semantics of the identification string. /// /// The transform identification string is key into a table of /// well-known transform definitions. /// internal const int TransformIdentifierTypes_PredefinedTransformName = 1; #region Version Methods ////// Read the version information that specifies the minimum versions of the /// DataSpaceManager software that can read, write, or update the data space /// information in this file. /// /// ////// If the format version information in the stream is corrupt. /// private void ReadDataSpaceVersionInformation(StorageInfo dataSpaceStorage) { if (_fileFormatVersion == null) { if (dataSpaceStorage.StreamExists( DataSpaceVersionName )) { StreamInfo versionStreamInfo = dataSpaceStorage.GetStreamInfo( DataSpaceVersionName ); using (Stream versionStream = versionStreamInfo.GetStream(FileMode.Open)) { _fileFormatVersion = FormatVersion.LoadFromStream(versionStream); // Transform Identifier: we preserve casing, but do case-insensitive comparison //Case-insensitive comparison. As per recommendations, we convert both strings //to Upper case and then compare with StringComparison.Ordinal if (!((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(_fileFormatVersion.FeatureIdentifier, DataSpaceVersionIdentifier)) { throw new FileFormatException( SR.Get(SRID.InvalidTransformFeatureName, _fileFormatVersion.FeatureIdentifier, DataSpaceVersionIdentifier)); } // If we ever write this version number out again, we will want to record // the fact that it was done by the current version of the Dataspace software. _fileFormatVersion.WriterVersion = DataSpaceCurrentWriterVersion; } } } } ////// If the DataSpace version information was not present in the file, we initialize it with the /// values for the current DataSpace software. /// If the information was present we should have already read it as a part of the ReadDataSpaceMap. /// private void EnsureDataSpaceVersionInformation() { if (_fileFormatVersion == null) { _fileFormatVersion = new FormatVersion( DataSpaceVersionIdentifier, DataSpaceCurrentWriterVersion, DataSpaceCurrentReaderVersion, DataSpaceCurrentUpdaterVersion ); } } ////// Verify that the current version of this class can read the DataSpace information in /// this file. /// ////// If the current version of this class cannot read the DataSpace information in this file. /// private void ThrowIfIncorrectReaderVersion() { EnsureDataSpaceVersionInformation(); if (!_fileFormatVersion.IsReadableBy(DataSpaceCurrentReaderVersion)) { throw new FileFormatException( SR.Get( SRID.ReaderVersionError, _fileFormatVersion.ReaderVersion, DataSpaceCurrentReaderVersion ) ); } } ////// Verify that the current version of this class can update the DataSpace information in /// this file. /// ////// If the current version of this class cannot update the DataSpace information in this file, /// or if the header information in the stream is corrupt. /// private void ThrowIfIncorrectUpdaterVersion() { EnsureDataSpaceVersionInformation(); if (!_fileFormatVersion.IsUpdatableBy(DataSpaceCurrentUpdaterVersion)) { throw new FileFormatException( SR.Get( SRID.UpdaterVersionError, _fileFormatVersion.UpdaterVersion, DataSpaceCurrentUpdaterVersion ) ); } } #endregion Version Methods } ////// Interface to be implemented by all data transform objects /// internal interface IDataTransform { ////// Whether this transform is ready to perform. If false, it needs /// further initialization. /// bool IsReady { get; } ////// Whether this transform is in a stable state /// bool FixedSettings { get; } ////// Data transform identifier object /// object TransformIdentifier { get; } ////// Given a stream for storing the encoded data, return a stream for /// manipulating the "cleartext" data. /// Stream GetTransformedStream( Stream encodedDataStream, IDictionary transformContext ); } ////// Internal class for passing arguments into event handlers /// internal class TransformInitializationEventArgs : EventArgs { IDataTransform dataInstance; string dataSpaceLabel; string streamPath; string transformLabel; internal TransformInitializationEventArgs( IDataTransform instance, string dataSpaceInstanceLabel, string transformedStreamPath, string transformInstanceLabel ) { dataInstance = instance; dataSpaceLabel = dataSpaceInstanceLabel; streamPath = transformedStreamPath; transformLabel = transformInstanceLabel; } ////// Reference to the data object that requires initialization /// internal IDataTransform DataTransform { get { return dataInstance; } } ////// Label for the data space whose initialization this is a part of /// internal string DataSpaceLabel { get { return dataSpaceLabel; } } ////// The path to the stream whose use initiated this process /// internal string Path { get { return streamPath; } } ////// Label for the transform object instance being initialized /// internal string TransformInstanceLabel { get { return transformLabel; } } } ////// An instance of this class is given to each transform object as a /// means for the transform object to interact with the environment /// provided by the data space manager. It is not mandatory for a /// transform object to keep a reference on the given TransformEnvironment /// object it may choose to discard it if there is no need to interact /// with the transform environment. /// internal class TransformEnvironment { DataSpaceManager transformHost; string transformLabel; ////// This object is only created internally by the data space manager. /// /// The data space manager who created this class /// Text label for this transform instance internal TransformEnvironment( DataSpaceManager host, string instanceLabel ) { transformHost = host; transformLabel = instanceLabel; } ////// Whether this transform demands that all instance data of other /// transform above it in the transform stack be transformed too /// internal bool RequireOtherInstanceData { get { return false; } set { transformHost.CheckDisposedStatus(); throw new NotSupportedException( SR.Get(SRID.NYIDefault)); } } ////// Whether this transform demands that its own instance data be /// free from other data transformation /// internal bool RequireInstanceDataUnaltered { get { return false; } set { transformHost.CheckDisposedStatus(); throw new NotSupportedException( SR.Get(SRID.NYIDefault)); } } ////// Whether this transform requests that its own instance data be /// processed by other transforms below it in the stack /// internal bool DefaultInstanceDataTransform { get { return false; } set { transformHost.CheckDisposedStatus(); throw new NotSupportedException( SR.Get(SRID.NYIDefault)); } } ////// In the transform definition hashtable, we only have the normalized /// label as the key. By digging our way to this property via the hashtable /// value, we can recover the original label. /// internal string TransformLabel { get { return transformLabel; } } ////// Returns the stream specific to this instance of the transform /// object, in which it can store any instance data it needs /// ///System.IO.Stream object for storing instance data internal Stream GetPrimaryInstanceData() { transformHost.CheckDisposedStatus(); return transformHost.GetPrimaryInstanceStreamOf( transformLabel ); } ////// When the primary instance data stream is insufficient, this returns /// a storage in which to store multiple streams that comprise the instance /// data that this transform object wants to store. /// ///StorageInfo pointing to the storage that this transform is free to use internal StorageInfo GetInstanceDataStorage() { transformHost.CheckDisposedStatus(); StorageInfo storageInfo = transformHost.GetInstanceDataStorageOf( transformLabel ); if (! storageInfo.Exists) { storageInfo.Create(); } return storageInfo; } } } // 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
- ControllableStoryboardAction.cs
- TaiwanCalendar.cs
- RichTextBox.cs
- PropertyEntry.cs
- HtmlLink.cs
- IERequestCache.cs
- Query.cs
- RuleInfoComparer.cs
- WebRequestModuleElementCollection.cs
- LoaderAllocator.cs
- OleAutBinder.cs
- RemotingException.cs
- DataObjectMethodAttribute.cs
- EntityDataSourceSelectedEventArgs.cs
- CaseKeyBox.xaml.cs
- RegistrySecurity.cs
- Point3DAnimation.cs
- PresentationSource.cs
- WebPartConnectionsCancelEventArgs.cs
- CustomAttributeFormatException.cs
- SupportsEventValidationAttribute.cs
- TextTreePropertyUndoUnit.cs
- WmlLinkAdapter.cs
- TypeConstant.cs
- ProfileInfo.cs
- XmlAnyElementAttribute.cs
- SafeProcessHandle.cs
- TypeUtil.cs
- RichTextBox.cs
- KeyGesture.cs
- XmlUnspecifiedAttribute.cs
- SqlInternalConnection.cs
- TrackingServices.cs
- ThumbAutomationPeer.cs
- Screen.cs
- LoginAutoFormat.cs
- ConfigurationValues.cs
- TextBlockAutomationPeer.cs
- EntityDesignerDataSourceView.cs
- Visual3DCollection.cs
- ButtonChrome.cs
- DataTableReaderListener.cs
- HyperLinkField.cs
- HtmlInputPassword.cs
- DataGridViewColumnStateChangedEventArgs.cs
- Directory.cs
- WindowsUpDown.cs
- SoapSchemaExporter.cs
- ReadOnlyHierarchicalDataSource.cs
- EventLogPermission.cs
- MessageSmuggler.cs
- Trace.cs
- PropertyCondition.cs
- XmlSchemaSequence.cs
- BrowserDefinitionCollection.cs
- UriTemplateTrieNode.cs
- PropertyEmitterBase.cs
- OdbcConnectionString.cs
- TiffBitmapEncoder.cs
- XmlAttributeCollection.cs
- Vector3D.cs
- CacheChildrenQuery.cs
- DataShape.cs
- BooleanToVisibilityConverter.cs
- XamlTypeMapper.cs
- EventHandlingScope.cs
- PathGeometry.cs
- SoapTypeAttribute.cs
- AnimatedTypeHelpers.cs
- DataGridViewCellPaintingEventArgs.cs
- InvokeHandlers.cs
- SafePEFileHandle.cs
- BamlRecords.cs
- DataError.cs
- TabRenderer.cs
- BypassElement.cs
- DirectoryNotFoundException.cs
- WindowsPen.cs
- WebException.cs
- NetCodeGroup.cs
- EventSetter.cs
- HttpRequest.cs
- ConnectionPoint.cs
- SafeJobHandle.cs
- GenericUI.cs
- CompositeDispatchFormatter.cs
- ApplicationManager.cs
- BitmapEffectDrawing.cs
- RegexRunnerFactory.cs
- HtmlToClrEventProxy.cs
- SelectionItemPattern.cs
- SpellCheck.cs
- ReflectPropertyDescriptor.cs
- PrintDialog.cs
- TextEndOfParagraph.cs
- SplineKeyFrames.cs
- listitem.cs
- XmlWriterTraceListener.cs
- MutexSecurity.cs
- VirtualPathUtility.cs