The Labs \ Source Viewer \ SSCLI \ System.Configuration.Internal \ WriteFileContext

  1. //------------------------------------------------------------------------------
  2. // <copyright file="WriteFileContext.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Configuration.Internal
  16. {
  17.     using System.Configuration;
  18.     using System.IO;
  19.     using System.Security.Permissions;
  20.     using System.Reflection;
  21.     using System.Threading;
  22.     using System.Security;
  23.     using System.CodeDom.Compiler;
  24.     using Microsoft.Win32;
  25.    
  26.     internal class WriteFileContext
  27.     {
  28.         private const int SAVING_TIMEOUT = 10000;
  29.         // 10 seconds
  30.         private const int SAVING_RETRY_INTERVAL = 100;
  31.         // 100 milliseconds
  32.         private static bool _osPlatformDetermined;
  33.         private static PlatformID _osPlatform;
  34.        
  35.         private TempFileCollection _tempFiles;
  36.         private string _tempNewFilename;
  37.         private string _templateFilename;
  38.        
  39.         internal WriteFileContext(string filename, string templateFilename)
  40.         {
  41.             string directoryname = UrlPath.GetDirectoryOrRootName(filename);
  42.            
  43.             _templateFilename = templateFilename;
  44.             _tempFiles = new TempFileCollection(directoryname);
  45.             try {
  46.                 _tempNewFilename = _tempFiles.AddExtension("newcfg");
  47.             }
  48.             catch {
  49.                 ((IDisposable)_tempFiles).Dispose();
  50.                 _tempFiles = null;
  51.                 throw;
  52.             }
  53.         }
  54.        
  55.         static WriteFileContext()
  56.         {
  57.             _osPlatformDetermined = false;
  58.         }
  59.        
  60.         internal string TempNewFilename {
  61.             get { return _tempNewFilename; }
  62.         }
  63.        
  64.         // Complete
  65.         //
  66.         // Cleanup the WriteFileContext object based on either success
  67.         // or failure
  68.         //
  69.         // Note: The current algorithm guarantess
  70.         // 1) The file we are saving to will always be present
  71.         // on the file system (ie. there will be no window
  72.         // during saving in which there won't be a file there)
  73.         // 2) It will always be available for reading from a
  74.         // client and it will be complete and accurate.
  75.         //
  76.         // ... This means that writing is a bit more complicated, and may
  77.         // have to be retried (because of reading lock), but I don't see
  78.         // anyway to get around this given 1 and 2.
  79.         //
  80.         internal void Complete(string filename, bool success)
  81.         {
  82.             try {
  83.                 if (success) {
  84.                     if (File.Exists(filename)) {
  85.                         // Test that we can write to the file
  86.                         ValidateWriteAccess(filename);
  87.                        
  88.                         // Copy Attributes from original
  89.                         DuplicateFileAttributes(filename, _tempNewFilename);
  90.                     }
  91.                     else {
  92.                         if (_templateFilename != null) {
  93.                             // Copy Acl from template file
  94.                             DuplicateTemplateAttributes(_templateFilename, _tempNewFilename);
  95.                         }
  96.                     }
  97.                    
  98.                     ReplaceFile(_tempNewFilename, filename);
  99.                    
  100.                     // Don't delete, since we just moved it.
  101.                     _tempFiles.KeepFiles = true;
  102.                 }
  103.             }
  104.             finally {
  105.                 ((IDisposable)_tempFiles).Dispose();
  106.                 _tempFiles = null;
  107.             }
  108.         }
  109.        
  110.         // DuplicateFileAttributes
  111.         //
  112.         // Copy all the files attributes that we care about from the source
  113.         // file to the destination file
  114.         //
  115.         private void DuplicateFileAttributes(string source, string destination)
  116.         {
  117.         }
  118.        
  119.         // DuplicateTemplateAttributes
  120.         //
  121.         // Copy over all the attributes you would want copied from a template file.
  122.         // As of right now this is just acl's
  123.         //
  124.         private void DuplicateTemplateAttributes(string source, string destination)
  125.         {
  126.         }
  127.        
  128.         // ValidateWriteAccess
  129.         //
  130.         // Validate that we can write to the file. This will enforce the ACL's
  131.         // on the file. Since we do our moving of files to replace, this is
  132.         // nice to ensure we are not by-passing some security permission
  133.         // that someone set (although that could bypass this via move themselves)
  134.         //
  135.         // Note: 1) This is really just a nice to have, since with directory permissions
  136.         // they could do the same thing we are doing
  137.         //
  138.         // 2) We are depending on the current behavior that if the file is locked
  139.         // and we can not open it, that we will get an UnauthorizedAccessException
  140.         // and not the IOException.
  141.         //
  142.         private void ValidateWriteAccess(string filename)
  143.         {
  144.             FileStream fs = null;
  145.            
  146.             try {
  147.                 // Try to open file for write access
  148.                 fs = new FileStream(filename, FileMode.Open, FileAccess.Write, FileShare.ReadWrite);
  149.             }
  150.             catch (UnauthorizedAccessException) {
  151.                 // Access was denied, make sure we throw this
  152.                 throw;
  153.             }
  154.             catch (IOException) {
  155.                 // Someone else was using the file. Since we did not get
  156.                 // the unauthorizedAccessException we have access to the file
  157.             }
  158.             catch (Exception) {
  159.                 // Unexpected, so just throw for safety sake
  160.                 throw;
  161.             }
  162.             finally {
  163.                 if (fs != null) {
  164.                     fs.Close();
  165.                 }
  166.             }
  167.         }
  168.        
  169.         // ReplaceFile
  170.         //
  171.         // Replace one file with another using MoveFileEx. This will
  172.         // retry the operation if the file is locked because someone
  173.         // is reading it
  174.         //
  175.         private void ReplaceFile(string Source, string Target)
  176.         {
  177.             bool WriteSucceeded = false;
  178.             int Duration = 0;
  179.            
  180.             WriteSucceeded = AttemptMove(Source, Target);
  181.            
  182.             // The file may be open for read, if it is then
  183.             // lets try again because maybe they will finish
  184.             // soon, and we will be able to replace
  185.             while (!WriteSucceeded && (Duration < SAVING_TIMEOUT) && File.Exists(Target) && !FileIsWriteLocked(Target)) {
  186.                
  187.                 Thread.Sleep(SAVING_RETRY_INTERVAL);
  188.                
  189.                 Duration += SAVING_RETRY_INTERVAL;
  190.                
  191.                 WriteSucceeded = AttemptMove(Source, Target);
  192.             }
  193.            
  194.             if (!WriteSucceeded) {
  195.                
  196.                 throw new ConfigurationErrorsException(SR.GetString(SR.Config_write_failed, Target));
  197.             }
  198.         }
  199.        
  200.         // AttemptMove
  201.         //
  202.         // Attempt to move a file from one location to another
  203.         //
  204.         // Return Values:
  205.         // TRUE - Move Successful
  206.         // FALSE - Move Failed
  207.         private bool AttemptMove(string Source, string Target)
  208.         {
  209.             bool MoveSuccessful = false;
  210.            
  211.             if (IsWinNT) {
  212.                
  213.                 // We can only call this when we have kernel32.dll
  214.                 MoveSuccessful = UnsafeNativeMethods.MoveFileEx(Source, Target, UnsafeNativeMethods.MOVEFILE_REPLACE_EXISTING);
  215.             }
  216.             else {
  217.                
  218.                 try {
  219.                    
  220.                     File.Move(Source, Target);
  221.                     MoveSuccessful = true;
  222.                 }
  223.                 catch {
  224.                    
  225.                     MoveSuccessful = false;
  226.                 }
  227.                
  228.             }
  229.            
  230.             return MoveSuccessful;
  231.         }
  232.        
  233.         // FileIsWriteLocked
  234.         //
  235.         // Is the file write locked or not?
  236.         //
  237.         private bool FileIsWriteLocked(string FileName)
  238.         {
  239.             Stream FileStream = null;
  240.             bool WriteLocked = true;
  241.            
  242.             if (!FileUtil.FileExists(FileName, true)) {
  243.                 // It can't be locked if it doesn't exist
  244.                 return false;
  245.             }
  246.            
  247.             try {
  248.                 // Try to open for shared reading
  249.                 FileStream = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
  250.                
  251.                 // If we can open it for shared reading, it is not
  252.                 // write locked
  253.                 WriteLocked = false;
  254.             }
  255.             finally {
  256.                 if (FileStream != null) {
  257.                    
  258.                     FileStream.Close();
  259.                     FileStream = null;
  260.                 }
  261.             }
  262.            
  263.             return WriteLocked;
  264.         }
  265.        
  266.         // IsWinNT
  267.         //
  268.         // Are we running in WinNT or not?
  269.         //
  270.         private bool IsWinNT {
  271.             get { return true; }
  272.         }
  273.     }
  274. }

Developer Fusion