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