DocumentStream.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / TrustUi / MS / Internal / documents / Application / DocumentStream.cs / 1 / DocumentStream.cs

                            //------------------------------------------------------------------------------ 
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved. 
//
//  
//  An XpsDocument stream represents the stream data for the document
//  regardless of implementation of the backing streams.  It has logical 
//  operations that allow elevating from Read to SafeWrite and editing in place. 
// 
// 
// History:
//  08/28/2005: [....]: Initial implementation.
//-----------------------------------------------------------------------------
 
#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings
 
using System; 
using System.Globalization;
using System.IO; 
using System.Security;
using System.Security.Permissions;
using System.Security.AccessControl;
using System.Windows.TrustUI; 
using MS.Internal.PresentationUI;
 
namespace MS.Internal.Documents.Application 
{
///  
/// An XpsDocument stream represents the stream data for the document
/// regardless of implementation of the backing streams.  It has logical
/// operations that allow elevating from Read to SafeWrite and editing in place.
///  
/// 
/// Responsibility: 
/// The class must hide the location and implemenation complexity of 
/// performing simple logical operations needed by the system.
/// 
/// Design Comments:
/// The need for this is primarly driven from two factors:
///
///  - Package which does not allow use to discard changes 
///
///  - RightsManagement where key changes make it impossible to edit a 
///    document in place 
/// 
internal sealed class DocumentStream : StreamProxy 
{
    #region Constructors
    //-------------------------------------------------------------------------
    // Constructors 
    //-------------------------------------------------------------------------
 
    ///  
    /// Constructs an DocumentStream.
    ///  
    /// The file to manage.
    /// The mode to open the file in.
    /// The access to open the file with.
    /// If a temporary file; the file we are based on. 
    /// 
    ///  
    /// Critical: 
    ///  - sets _original & _xpsFileToken which are critical data
    ///  
    [SecurityCritical]
    private DocumentStream(
        CriticalFileToken xpsFileToken,
        Stream dataSource, 
        DocumentStream original)
        : base(dataSource) 
    { 
        _original = original;
        _xpsFileToken = xpsFileToken; 
    }
    #endregion Constructors

    #region StreamProxy Overrides 
    //--------------------------------------------------------------------------
    // StreamProxy Overrides 
    //------------------------------------------------------------------------- 

    ///  
    /// Will close the document stream and clean up any temporary
    /// files.
    /// 
    ///  
    /// We are overriding StreamProxy's implementation which calls
    /// Close on the target of the proxy (FileStream), which means 
    /// our Dispose(boolean) would not get called.  This way the 
    /// 'this' in Stream.Close will refer to 'DocumentStream'
    /// not target's (FileStream) and we will get called. 
    /// 
    public override void Close()
    {
        this.Dispose(true); 
        GC.SuppressFinalize(this);
    } 
    #endregion StreamProxy Overrides 

    #region Internal Methods 
    //--------------------------------------------------------------------------
    // Internal Methods
    //--------------------------------------------------------------------------
 
    /// 
    /// Will create an XpsDocument by copying this one to a target file and 
    /// returning a stream corresponding to the new file. 
    /// 
    ///  
    /// 
    /// The token for the target file.
    /// An DocumentStream.
    ///  
    /// Critical:
    ///  - accesses the CriticalFileToken 
    ///  - asserts FileIOPermission 
    ///  - constructs DocumentStream which is critical
    /// TreatAsSafe: 
    ///  - CriticalFileToken is not leaked (DocumentStream is trusted)
    ///  - caller must had CriticalFileToken and thus satisfied
    ///     the FileIOPermission demand
    ///  - document is an XpsDocument a non executable extension that is 
    ///     safe to run in PartialTrust
    ///  
    [SecurityCritical, SecurityTreatAsSafe] 
    internal DocumentStream Copy(CriticalFileToken copiesToken)
    { 
        DocumentStream result;
        FileStream target = null;

        bool isFileSource = (_xpsFileToken != null); 

        Invariant.Assert(copiesToken != null, "No target file to which to copy."); 
 
        ThrowIfInvalidXpsFileForSave(copiesToken.Location);
 
        string sourcePath = string.Empty;
        string copiesPath = copiesToken.Location.LocalPath;

        PermissionSet copy = new PermissionSet(null); 

        // if the source is a file, we need to release our lock on the source 
        // file and also assert for permissions to read the file 
        if (isFileSource)
        { 
            Target.Close();

            sourcePath = _xpsFileToken.Location.LocalPath;
 
            copy.AddPermission(new FileIOPermission(
                FileIOPermissionAccess.Read, sourcePath)); 
        } 

        copy.AddPermission(new FileIOPermission( 
            FileIOPermissionAccess.Read | FileIOPermissionAccess.Write,
            copiesPath));
        copy.Assert(); //BlessedAssert
        try 
        {
            // if the source is a file, file copy is the fastest 
            if (isFileSource) 
            {
                File.Copy(sourcePath, copiesPath, true); 

                // If the original file was marked read-only, the copy will be read-only as
                // well.  However, the copy is done for the purpose of creating a new file,
                // so it should not be marked read-only. 
                FileAttributes attrib = File.GetAttributes(copiesPath);
                if ((attrib & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) 
                { 
                    File.SetAttributes(copiesPath, attrib ^ FileAttributes.ReadOnly);
                } 

                // open the destination file that was just created by File.Copy
                target = new FileStream(
                    copiesPath, 
                    FileMode.Open,
                    FileAccess.ReadWrite, 
                    FileShare.None); 
            }
            else 
            {
                // open the destination file for create; we will copy the
                // source stream's data to the new stream outside the assert
                target = new FileStream( 
                    copiesPath,
                    FileMode.Create, 
                    FileAccess.ReadWrite, 
                    FileShare.None);
            } 
        }
        // Since we have already closed the original file, we need to reopen it if we
        // fail to copy the file or open the new file.  After doing so, we rethrow the
        // original exception so it can be handled at a higher level. 
#pragma warning suppress 56500 // suppress PreSharp Warning 56500: Avoid `swallowing errors by catching non-specific exceptions..
        catch 
        { 
            if (isFileSource)
            { 
                Trace.SafeWrite(
                    Trace.File,
                    "File copy failed -- reopening original file.");
                try 
                {
                    Target = new FileStream( 
                        sourcePath, 
                        FileMode.Open,
                        FileAccess.Read, 
                        FileShare.Read);
                }
                // If we fail to reopen the original file, rethrow an exception to
                // indicate this specific error. 
#pragma warning suppress 56500 // suppress PreSharp Warning 56500: Avoid `swallowing errors by catching non-specific exceptions..
                catch (Exception e) 
                { 
                    Trace.SafeWrite(
                        Trace.File, 
                        "Unable to reopen original file.");

                    throw new UnauthorizedAccessException(
                        SR.Get(SRID.DocumentStreamCanNoLongerOpen), e); 
                }
            } 
            throw; 
        }
        finally 
        {
            PermissionSet.RevertAssert();
        }
 
        if (isFileSource)
        { 
            Trace.SafeWrite(Trace.File, "Performed a file copy from source."); 

            // reacquire our stream 
            ReOpenWriteable();
        }
        else
        { 
            // if the source wasn't a file, we want to copy the stream now
            StreamHelper.CopyStream(this, target); 
            Trace.SafeWrite(Trace.File, "Performed a stream copy from source."); 
        }
 
        //---------------------------------------------------------------------
        // Create the DocumentStream
        result = new DocumentStream(copiesToken, target, this);
 
        result.DeleteOnClose = false;
 
        Trace.SafeWrite(Trace.File, "Created copy to file {0}.", copiesToken.Location); 

        return result; 
    }

    /// 
    /// Will create an DocumentStream backed by a tempoary file. 
    /// 
    ///  
    /// We prefer to create the temporary file in the same location as the 
    /// source to inherit the folders attributes and security.
    /// 
    /// If we can not we will use a system generated file.
    /// 
    /// When true we will copy the source file if
    /// possible.  You must check for a non-zero result on the returning stream 
    /// to determin success.
    /// An DocumentStream. 
    ///  
    /// Critical:
    ///  - performs many asserts 
    ///  - calls Critical methods MakeTempFile*
    ///  - uses _xpsFileToken.Location which is security critical
    ///  - constructs a DocumentStream
    /// NotSafe: 
    ///  - repeated calls can DoS disk space
    ///  
    [SecurityCritical] 
    internal DocumentStream CreateTemporary(bool copyOriginal)
    { 
        CriticalFileToken tempToken = null;
        DocumentStream result = null;

        FileStream temporary = null; 
        bool isFileSource = (_xpsFileToken != null);
 
        //---------------------------------------------------------------------- 
        // Open File in Same Location (if possible)
        if (isFileSource) 
        {
            MakeTempFile(true, out temporary, out tempToken);
        }
 
        //---------------------------------------------------------------------
        // Open File in System Generated Location 
        if (tempToken == null) 
        {
            // 



 
            MakeTempFile(false, out temporary, out tempToken);
        } 
 
        //---------------------------------------------------------------------
        // File Was Opened 
        if ((temporary != null) && (tempToken != null))
        {
            //-----------------------------------------------------------------
            // Copy Data 
            if (copyOriginal)
            { 
                // We use a native File.Copy if possible because this is 
                // most performant.  This is only possible if the source is file
                // based. 
                if (isFileSource)
                {
                    string sourcePath = _xpsFileToken.Location.LocalPath;
                    string tempPath = tempToken.Location.LocalPath; 

                    // file copy is the fastest 
                    PermissionSet copy = new PermissionSet(null); 
                    copy.AddPermission(new FileIOPermission(
                        FileIOPermissionAccess.Read, sourcePath)); 
                    copy.AddPermission(new FileIOPermission(
                        FileIOPermissionAccess.Read | FileIOPermissionAccess.Write,
                        tempPath));
                    copy.Assert(); //BlessedAssert 
                    try
                    { 
                        temporary.Close(); 

                        File.Copy(sourcePath, tempPath, true); 

                        temporary = new FileStream(
                            tempPath,
                            FileMode.Open, 
                            FileAccess.ReadWrite,
                            FileShare.None); 
 
                        // we did the copy
                        copyOriginal = false; 
                    }
                    finally
                    {
                        PermissionSet.RevertAssert(); 
                    }
                    Trace.SafeWrite(Trace.File, "Performed a file copy from source."); 
                } 
                else
                { 
                    StreamHelper.CopyStream(
                        this, temporary);
                    Trace.SafeWrite(Trace.File, "Performed a stream copy from source.");
                } 
            }
 
            //------------------------------------------------------------------ 
            // Create the DocumentStream
            result = new DocumentStream( 
                tempToken, temporary, this);

            result.DeleteOnClose = true;
 
            Trace.SafeWrite(Trace.File, "Created temporary file {0}.", tempToken.Location);
        } 
        else 
        {
            // rescind consent if any was given 
            tempToken = null;
            Trace.SafeWrite(Trace.File, "Unable to create a temporary file.  Caller is expected to disable edits.");
        }
        return result; 
    }
 
    ///  
    /// Checks if there is a read-only file at the path stored in the given
    /// CriticalFileToken. 
    /// 
    /// A token containing the path of the file
    /// True if a file exists and is read-only
    ///  
    /// Critical
    ///  1) Asserts for FileIOPermission 
    ///  
    [SecurityCritical]
    internal static bool IsReadOnly(CriticalFileToken fileToken) 
    {
        string path = fileToken.Location.LocalPath;
        FileAttributes attributes = FileAttributes.Normal;
 
        FileIOPermission permission = new FileIOPermission(
            FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, 
            path); 
        permission.Assert();
        try 
        {
            if (File.Exists(path))
            {
                attributes = File.GetAttributes(path); 
            }
        } 
        finally 
        {
            FileIOPermission.RevertAssert(); 
        }

        return ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly);
    } 

    ///  
    /// Will open an XpsDocument and return this stream. 
    /// 
    ///  
    /// 
    /// The document to open.
    /// When true will open the file writeable.
    /// Default is read-only. 
    /// An DocumentStream.
    ///  
    /// Critical: 
    ///  - accesses the CriticalFileToken
    /// TreatAsSafe: 
    ///  - CriticalFileToken is not leaked (DocumentStream is trusted)
    ///  - caller must had CriticalFileToken and thus satisfied
    ///     the FileIOPermission demand
    ///  - document is an XpsDocument a non executable extension that is 
    ///     safe to run in PartialTrust
    ///  
    [SecurityCritical, SecurityTreatAsSafe] 
    internal static DocumentStream Open(
        FileDocument document, bool writeable) 
    {
        return Open(document.SourceToken, writeable);
    }
 
    /// 
    /// Will open an XpsDocument and return this stream. 
    ///  
    /// 
    ///  
    /// The token for the file to open.
    /// When true will open the file writeable.
    /// Default is read-only.
    /// An DocumentStream. 
    /// 
    /// Critical: 
    ///  - accesses the CriticalFileToken 
    ///  - asserts FileIOPermission
    ///  - constructs DocumentStream which is critical 
    /// TreatAsSafe:
    ///  - CriticalFileToken is not leaked (DocumentStream is trusted)
    ///  - caller must had CriticalFileToken and thus satisfied
    ///     the FileIOPermission demand 
    ///  - document is an XpsDocument a non executable extension that is
    ///     safe to run in PartialTrust 
    ///  
    [SecurityCritical, SecurityTreatAsSafe]
    internal static DocumentStream Open(CriticalFileToken xpsFileToken, bool writeable) 
    {
        ThrowIfInvalidXpsFileForOpen(xpsFileToken.Location);

        FileIOPermissionAccess permission = FileIOPermissionAccess.Read; 
        FileAccess access = FileAccess.Read;
 
        if (writeable) 
        {
            permission |= FileIOPermissionAccess.Write; 
            access |= FileAccess.Write;
        }

        FileStream dataSource; 
        string sourcePath = xpsFileToken.Location.LocalPath;
 
        new FileIOPermission(permission, sourcePath).Assert(); //BlessedAssert 
        try
        { 
            dataSource = new FileStream(
                sourcePath,
                FileMode.OpenOrCreate,
                access, 
                (access == FileAccess.Read) ? FileShare.Read : FileShare.None);
        } 
        finally 
        {
            FileIOPermission.RevertAssert(); 
        }

        Trace.SafeWrite(Trace.File, "Opened file {0}.", sourcePath);
 
        return new DocumentStream(
            xpsFileToken, dataSource, null); 
    } 

    ///  
    /// Will open an create a DocumentStream using the provided stream.
    /// 
    /// 
    /// The existing stream to use. 
    /// An DocumentStream.
    ///  
    /// Critical: 
    ///  - constructs DocumentStream which is critical
    /// TreatAsSafe: 
    ///  - stream is already open
    ///  - CriticalFileToken is null
    /// 
    [SecurityCritical, SecurityTreatAsSafe] 
    internal static DocumentStream Open(Stream existing)
    { 
        if (existing == null) 
        {
            throw new ArgumentNullException("existing"); 
        }

        Trace.SafeWrite(
            Trace.File, 
            "Opened {0}#{1} as existing stream.",
            existing, 
            existing.GetHashCode()); 

        return new DocumentStream( 
            null, existing, null);
    }

    ///  
    /// Will re-open the file writeable and update the underlying stream.
    ///  
    ///  
    /// True if the operation succeeded.
    ///  
    /// 
    /// Critical:
    ///  - asserts FileIOPermission
    ///  - calls _xpsFileToken.Location which is critical 
    /// TreatAsSafe:
    ///  - simply upgrades read mode to read/write; caller already satsified 
    ///     a demand for read/write by using CriticalFileToken our comparee 
    ///  - document is an XpsDocument a non executable extension that is
    ///     safe to run in PartialTrust 
    /// 
    [SecurityCritical, SecurityTreatAsSafe]
    internal bool ReOpenWriteable()
    { 
        if ((_xpsFileToken == null) || (!_xpsFileToken.Location.IsFile))
        { 
            return false; 
        }
 
        bool success = false;

        //---------------------------------------------------------------------
        // Release Existing Locks (so we open with write) 
        if (Target != null)
        { 
            Target.Close(); 
        }
 
        //----------------------------------------------------------------------
        // Open Writable (if it fails re-open for Read)
        FileStream fs = null;
        // want to use same local values for assert and open 
        string path = _xpsFileToken.Location.LocalPath;
 
        new FileIOPermission( 
            FileIOPermissionAccess.Read | FileIOPermissionAccess.Write,
            path) 
        .Assert(); //BlessedAssert
        try
        {
            Exception exception = null; 

            try 
            { 
                fs = new FileStream(
                    path, 
                    FileMode.Open,
                    FileAccess.Read | FileAccess.Write,
                    FileShare.None);
 
                success = true;
            } 
            // thrown on file is locked by others or we can't modify the file 
            catch (IOException ioe)
            { 
                exception = ioe;
            }
            // thrown on file is locked by others or we can't modify the file
            catch (UnauthorizedAccessException uae) 
            {
                exception = uae; 
            } 

            if (!success) 
            {
                if (exception != null)
                {
                    Trace.SafeWrite( 
                        Trace.File,
                        "Failed to reopen {0} writeable.\n{1}", 
                        path, 
                        exception);
                } 

                // on failure try to regain our read access as we had before
                fs = new FileStream(
                    path, 
                    FileMode.Open,
                    FileAccess.Read, 
                    FileShare.Read); 
            }
        } 
        finally
        {
            FileIOPermission.RevertAssert();
        } 

        Invariant.Assert( 
            fs != null, 
            "ReOpenWriteable: Can no longer open file.");
 
        if (success)
        {
            Trace.SafeWrite(Trace.File, "Reopened {0} writeable.", path);
        } 
        else
        { 
            Trace.SafeWrite(Trace.File, "Reopened {0} read only.", path); 
        }
 
        //----------------------------------------------------------------------
        // Update Reference
        Target = fs;
 
        return success;
    } 
 
    /// 
    /// Will swap the temporary file with the original file. 
    /// 
    /// 
    /// 
    ///  
    /// False if the operation failed.
    ///  
    /// This method is inplace to work around issues with re-publishing 
    /// XpsDocuments into the same file.  The intended use for the method is
    /// to logically allow in place editing for the user. 
    ///
    /// After use this object is unusable and should be disposed as the
    /// temporary file is gone; it has become the original. In the event of an
    /// error while swapping the file, the file no longer becomes the original, 
    /// but this object still becomes unusable.
    ///  
    ///  
    /// Critical:
    ///  - accesses critical data _xpsFileToken.Location 
    ///  - performs many asserts to manipulate this and the original file
    /// TreatAsSafe:
    ///  - source data is critical and required Read & SafeWrite asserts
    ///  - document is an XpsDocument a non executable extension that is 
    ///    safe to run in PartialTrust
    ///  
    [SecurityCritical, SecurityTreatAsSafe] 
    internal bool SwapWithOriginal()
    { 
        bool success = false;

        if (_original == null)
        { 
            throw new InvalidOperationException(
                SR.Get(SRID.DocumentStreamMustBeTemporary)); 
        } 
        if (_original._xpsFileToken == null)
        { 
            throw new InvalidOperationException(
                SR.Get(SRID.DocumentStreamMustBeFileSource));
        }
        if (_xpsFileToken == null) 
        {
            throw new InvalidOperationException( 
                SR.Get(SRID.DocumentStreamMustBeFileSource)); 
        }
 
        Trace.SafeWrite(
            Trace.File,
            "Begining file swap between {0} and {1}.",
            _xpsFileToken.Location, 
            _original._xpsFileToken.Location);
 
        ThrowIfInvalidXpsFileForSave(_xpsFileToken.Location); 
        ThrowIfInvalidXpsFileForOpen(_original._xpsFileToken.Location);
 
        string original = _original._xpsFileToken.Location.LocalPath;

        //---------------------------------------------------------------------
        // Capture Attributes & ACLs (so we can apply to copied file) 
        FileAttributes originalAttribs;
        FileSecurity originalSecurity; 
 
        new FileIOPermission(
            FileIOPermissionAccess.Read, 
            AccessControlActions.View,
            original)
        .Assert(); //BlessedAssert
        try 
        {
            originalAttribs = File.GetAttributes(original); 
            originalSecurity = File.GetAccessControl( 
                original, AccessControlSections.Access);
        } 
        finally
        {
            FileIOPermission.RevertAssert();
        } 

        //---------------------------------------------------------------------- 
        // Release Existing Locks (so we can manipulate files) 

        // Cache the original stream position so we can restore it on failure 
        long originalPosition = _original.Position;
        _original.Target.Close();

        Target.Close(); 

        //--------------------------------------------------------------------- 
        // Swap Files On Disk 
        try
        { 
            RobustFileMove();
            success = true;
        }
        catch (IOException) 
        {
            Trace.SafeWrite( 
                Trace.File, "File could not be swapped with original."); 
        }
        catch (UnauthorizedAccessException) 
        {
            Trace.SafeWrite(
                Trace.File, "File could not be swapped with original.");
        } 

        //--------------------------------------------------------------------- 
        // Set Attributes & ACLs 
        // (logically user expected an edit thus attributes should be same)
        if (success) 
        {
            new FileIOPermission(
                FileIOPermissionAccess.Write,
                AccessControlActions.Change, 
                original)
            .Assert(); //BlessedAssert 
            try 
            {
                File.SetAttributes(original, originalAttribs); 
                File.SetAccessControl(original, originalSecurity);
            }
            // A failure to set attributes or ACLs is not fatal to the application, so we
            // do not want it to crash the application for the user, and thus catch all 
            // exceptions here.
#pragma warning suppress 56500 // suppress PreSharp Warning 56500: Avoid `swallowing errors by catching non-specific exceptions.. 
            catch 
            {
                // 
                Trace.SafeWrite(
                    Trace.File,
                    "File attributes or permissions could not be set.");
            } 
            finally
            { 
                FileIOPermission.RevertAssert(); 
            }
 
            Trace.SafeWrite(
                Trace.File, "File was successfully swapped with original.");
        }
        else 
        {
            new FileIOPermission( 
                FileIOPermissionAccess.Read, 
                original)
            .Assert(); //BlessedAssert 
            try
            {
                // try to regain our read access as we had before
                _original.Target = new FileStream( 
                    original,
                    FileMode.Open, 
                    FileAccess.Read, 
                    FileShare.Read);
            } 
            // In this case we catch all exceptions and then rethrow a new exception --
            // always using the same exception as a wrapper allows higher level
            // routines to respond to a failure specifically in this section.
#pragma warning suppress 56500 // suppress PreSharp Warning 56500: Avoid `swallowing errors by catching non-specific exceptions.. 
            catch (Exception e)
            { 
                Trace.SafeWrite( 
                    Trace.File,
                    "SwapWithOriginal has left us in an unusable state."); 

                throw new UnauthorizedAccessException(
                    SR.Get(SRID.DocumentStreamCanNoLongerOpen), e);
            } 
            finally
            { 
                FileIOPermission.RevertAssert(); 
            }
 
            // CanRead will be true only if the stream was opened; Close set it
            // to false earlier
            if (_original.CanRead)
            { 
                // Restore the position to prevent errors for callers dependent
                // on position 
                _original.Position = originalPosition; 
            }
 
            Trace.SafeWrite(
                Trace.File, "File swap was unsuccessful; restored original stream.");
        }
 
        return success;
    } 
    #endregion Internal Methods 

    #region Internal Properties 
    //-------------------------------------------------------------------------
    // Internal Properties
    //--------------------------------------------------------------------------
 
    /// 
    /// When true will delete the file on close. 
    ///  
    /// 
    /// Critical: 
    ///  - accesses critical field _deleteOnClose
    /// TreatAsSafe:
    ///  - get is safe by definition (see field)
    /// NotSafe: 
    ///  - set is not safe by definition (see field)
    ///  
    internal bool DeleteOnClose 
    {
        [SecurityCritical, SecurityTreatAsSafe] 
        get { return _deleteOnClose; }

        [SecurityCritical]
        set { _deleteOnClose = value; } 
    }
    #endregion Internal Properties 
 
    #region IDisposable Members
    //------------------------------------------------------------------------- 
    // IDisposable Members
    //--------------------------------------------------------------------------

    ///  
    /// Critical:
    ///  - accesses CriticalFileToken 
    ///  - asserts for FileIO 
    /// TreatAsSafe:
    ///  - we are deleting a file we already have writes to 
    ///  - the specific file location is protected critical data we can
    ///    treat as safe
    /// 
    [SecurityCritical, SecurityTreatAsSafe] 
    protected override void Dispose(bool disposing)
    { 
        Trace.SafeWrite( 
            Trace.File,
            "{0}({1}).Dispose({2}) called {3} delete.", 
            this,
            (_xpsFileToken != null) ? _xpsFileToken.Location.ToString() : string.Empty,
            disposing,
            DeleteOnClose && disposing ? "should" : "should not"); 

        try 
        { 
            // this is StreamProxy who first disposes its Stream
            // base, then the target of proxy (FileStream) in this 
            // case thus releasing the locks
            base.Dispose(disposing);
        }
        finally 
        {
            if (disposing) 
            { 
                if (DeleteOnClose)
                { 
                    string path = _xpsFileToken.Location.LocalPath;

                    new FileIOPermission(FileIOPermissionAccess.Write, path)
                        .Assert(); //BlessedAssert 
                    try
                    { 
                        Trace.SafeWrite( 
                            Trace.File,
                            "Attempting delete of {0}.", 
                            path);

                        File.Delete(path);
                    } 
                    catch (UnauthorizedAccessException uae)
                    { 
                        Trace.SafeWrite( 
                            Trace.File,
                            "Delete of temporary file {0} failed.\n{1}", 
                            path,
                            uae);

                        // do nothing intentionally if we can't we should still 
                        // shutdown gracefully
                    } 
                    finally 
                    {
                        FileIOPermission.RevertAssert(); 
                    }
                }
            }
        } 
    }
    #endregion IDisposable Members 
 
    #region Private Methods
    //-------------------------------------------------------------------------- 
    // Private Methods
    //-------------------------------------------------------------------------

    ///  
    /// Will create a temporary file.
    ///  
    ///  
    /// 
    /// When true will attempt to use the same folder 
    /// as the orginal file.
    /// The stream for the temporary file.
    /// The file token for the temporary file.
    ///  
    /// Critical:
    ///  - calls MakeTemporaryFileName 
    ///  - performs FileIO asserts 
    ///  - constructs a CriticalFileToken
    ///  
    [SecurityCritical]
    private void MakeTempFile(
        bool inSameFolder,
        out FileStream temporary, 
        out CriticalFileToken tempToken)
    { 
        temporary = null; 
        tempToken = null;
 
        // Will retry three times for a temp file in the folder.
        // The user could have two copies open and be saving before we
        // would fall back.  The reason for not making this a large
        // number is the user may not have access to the folder the attemps 
        // would be futile and degrade the experience with delays
        for (int i = 0; i <= 3; i++) 
        { 
            Uri location = MakeTemporaryFileName(inSameFolder, i);
            string tempPath = location.LocalPath; 

            ThrowIfInvalidXpsFileForSave(location);

            new FileIOPermission( 
                FileIOPermissionAccess.Read
                    | FileIOPermissionAccess.Write, 
                tempPath) 
            .Assert(); // BlessedAssert
            try 
            {
                temporary = new FileStream(
                    tempPath,
                    FileMode.Create, 
                    FileAccess.ReadWrite,
                    FileShare.None); 
 
                File.SetAttributes(
                    tempPath, 
                    FileAttributes.Hidden | FileAttributes.Temporary);

                tempToken = new CriticalFileToken(location);
            } 
            catch (IOException io)
            { 
                Trace.SafeWrite( 
                    Trace.File,
                    "Temporary file {0} is likely in use.\n{1}", 
                    temporary,
                    io);
            }
            catch (UnauthorizedAccessException io) 
            {
                Trace.SafeWrite( 
                    Trace.File, 
                    "Temporary file {0} is likely in use.\n{1}",
                    temporary, 
                    io);
            }
#pragma warning suppress 56500 // suppress PreSharp Warning 56500: Avoid `swallowing errors by catching non-specific exceptions..
            catch (Exception exception) 
            {
                // not editing is not a critical failure for the application 
                // so we can handle this method failing any event 
                Trace.SafeWrite(
                    Trace.File, 
                    "Giving up on temp file.\n{0}",
                    exception);
                break;
            } 
            finally
            { 
                FileIOPermission.RevertAssert(); 
            }
            if (tempToken != null) 
            {
                break;
            }
 
#if DEBUG
            Invariant.Assert( 
                ((i != 3) || (temporary != null) || (inSameFolder)), 
                "Unable to create a temp file.\n"
                + "Unless IE Cache is read-only we have a defect."); 
#endif
        }
    }
 
    /// 
    /// Will create temporary file name based on this file. 
    ///  
    /// 
    ///  
    /// 
    /// Critical:
    ///  - accesses critical data _xpsFileToken.Location
    ///  - accesses critical data MS.Win32.WinInet.InternetCacheFolder 
    ///  - caller should not leak this data outside of class
    ///  
    [SecurityCritical] 
    private Uri MakeTemporaryFileName(bool inSameFolder, int generation)
    { 
        string path, file;

        if (inSameFolder)
        { 
            path = _xpsFileToken.Location.LocalPath;
            file = Path.GetFileNameWithoutExtension(path); 
        } 
        else
        { 
            path = MS.Win32.WinInet.InternetCacheFolder.LocalPath;
            file = this.GetHashCode().ToString(CultureInfo.InvariantCulture);
        }
 
        string temp = string.Format(
            CultureInfo.CurrentCulture, 
            "{0}{1}~{2}{3}{4}", 
            Path.GetDirectoryName(path),
            Path.DirectorySeparatorChar, 
            generation == 0 ?
                string.Empty :
                generation.ToString(CultureInfo.CurrentCulture)
                + "~", 
            file,
            XpsFileExtension); 
        return new Uri(temp); 
    }
 
    /// 
    /// Will safely move the temp file to the comparee file.
    /// 
    ///  
    /// Design is simple Source needs to be moved to Target ensuring
    /// no data loss on errror. 
    /// 
    /// If Source exists rename it (creating Backup)
    /// Move Source to Target 
    /// If error occurs Move Backup to Source (resore from Backup)
    /// If all was good (delete Backup)
    ///
    /// This design incures trival I/O costs by using moves. 
    /// 
    ///  
    /// Critical: 
    ///  - we assert for the permissions to do a three way file move to
    ///     ensure that if we try to overwrite and something goes wrong 
    ///     the original file is replaced
    /// 
    [SecurityCritical]
    private void RobustFileMove() 
    {
        string sourceFile = _xpsFileToken.Location.LocalPath; 
        string targetFile = _original._xpsFileToken.Location.LocalPath; 
        string backupFile = targetFile + ".bak";
 
        bool backupExists = false;
        FileAttributes targetAttributes = FileAttributes.Normal;

        new FileIOPermission( 
                FileIOPermissionAccess.Read |
                FileIOPermissionAccess.Write, 
                new string[] { 
            targetFile, backupFile,sourceFile})
        .Assert(); //BlessedAssert 
        try
        {
            // back up the file if we will be overwriting
            if (File.Exists(targetFile)) 
            {
                Trace.SafeWrite( 
                    Trace.File, 
                    "Attempting backup of {0} due to overwrite case.",
                    targetFile); 

                // GetTempPath will create a zero byte file
                // and move will fail if it exists
 
                // If we already have a backup file, we'll delete it first.
                if( File.Exists( backupFile ) ) 
                { 
                    // Reset attributes so we can delete even read-only files
                    File.SetAttributes(backupFile, FileAttributes.Normal); 
                    File.Delete(backupFile);
                }

                // Save the original file attributes so we can restore them if 
                // the temp file copy fails.
                targetAttributes = File.GetAttributes(targetFile); 
                File.Move(targetFile, backupFile); 

                Trace.SafeWrite( 
                    Trace.File,
                    "Created backup of {0} at {1}.",
                    targetFile,
                    backupFile); 
                backupExists = true;
            } 
 
            try
            { 
                // Set the attributes on the backup to Normal so that we can
                // successfully delete it after the copy.
                File.SetAttributes(backupFile, FileAttributes.Normal);
 
                // try to move (save) the temp file to its final location
                File.Move(sourceFile, targetFile); 
                Trace.SafeWrite( 
                    Trace.File,
                    "Moved(Saved) {0} as {1}.", 
                    sourceFile,
                    targetFile);
            }
            catch 
            {
                // catching everything as we do not care why we failed 
                // regardless we want to restore the original file 

                if (backupExists) 
                {
                    // restore original on failure
                    File.Move(backupFile, targetFile);
                    Trace.SafeWrite( 
                        Trace.File,
                        "Restored {0} from {1}, due to exception.", 
                        targetFile, 
                        backupFile);
 
                    File.SetAttributes(targetFile, targetAttributes);
                }
                // pass the error up as we can't fix it
                throw; 
            }
 
            try 
            {
                // Try to delete the backup file 
                if (backupExists)
                {
                    File.Delete(backupFile);
                    backupExists = false; 
                    Trace.SafeWrite(
                        Trace.File, 
                        "Removed backup {0}.", 
                        backupFile);
                } 
            }
#pragma warning suppress 56500 // suppress PreSharp Warning 56500: Avoid `swallowing errors by catching non-specific exceptions..
            catch(Exception e)
            { 
                // We were unable to delete the backup file -- this is not fatal.
                Trace.SafeWrite( 
                    Trace.File, 
                     "Unable to remove backup {0}.\n{1}",
                    backupFile, 
                    e);
            }
        }
        finally 
        {
            FileIOPermission.RevertAssert(); 
        } 
    }
 
    /// 
    /// Will throw if location is not a XpsDocument file.
    /// 
    ///  
    /// 
    ///  
    ///  
    /// 
    /// Critical: 
    ///  - safety decisions are made based on the class not throwing
    ///  - marked to ensure review & auditing of changes to extension
    ///    checking
    /// TreatAsSafe: 
    ///  - merely validates data
    ///  
    [SecurityCritical, SecurityTreatAsSafe] 
    private static void ThrowIfInvalidXpsFileForSave(Uri location)
    { 
        ThrowIfInvalidXpsFileForOpen(location);

        if (!Path.GetExtension(location.LocalPath).Equals(
            XpsFileExtension, StringComparison.OrdinalIgnoreCase)) 
        {
            throw new InvalidDataException( 
                SR.Get(SRID.DocumentStreamMustBeXpsFile)); 
        }
    } 

    /// 
    /// Will throw if location is not a valid file.
    ///  
    /// 
    ///  
    private static void ThrowIfInvalidXpsFileForOpen(Uri location) 
    {
        if (location == null) 
        {
            throw new ArgumentNullException("location");
        }
        if (!location.IsFile) 
        {
            throw new InvalidOperationException( 
                SR.Get(SRID.DocumentStreamMustBeFileSource)); 
        }
    } 
    #endregion

    #region Private Fields
    //-------------------------------------------------------------------------- 
    // Private Fields
    //------------------------------------------------------------------------- 
 
    /// 
    /// Critical: 
    ///  - keep this ReadOnly for security reasons
    ///  - mitigates several risks by ensuring only safe file types are opened
    /// TreatAsSafe:
    ///  - value is set only once from assembly resources 
    /// 
    [FriendAccessAllowed, SecurityCritical, SecurityTreatAsSafe] 
    internal static readonly string XpsFileExtension = 
        SR.Get(SRID.FileManagementSaveExt);
 
    /// 
    /// Critical:
    ///  - malicious setting can cause loss of data (DoS)
    ///  
    [SecurityCritical]
    bool _deleteOnClose; 
 
    /// 
    /// If not null, this file is a temporary file based on this one. 
    /// 
    /// 
    /// Temporary files should be deleted on disposed, the value of this field
    /// is used for that decision. 
    /// 
    ///  
    /// Critical: 
    ///  - we have methods that will use this value to overwrite the location
    ///     of this file; we want to audit setting it 
    /// 
    [SecurityCritical]
    DocumentStream _original;
 
    /// 
    /// The file we are managing. 
    ///  
    /// 
    /// Critical: 
    ///  - token should not leak outside of class
    /// 
    [SecurityCritical]
    CriticalFileToken _xpsFileToken; 
    #endregion
} 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK