public void BadChars() { Assert.Throws<ArgumentException>(() => { var state = new FileState("|"); var time = state.LastWriteTime; } ); }
/// <summary> /// Cached implementation. Given a path, crack it open and retrieve runtimeversion for the assembly. /// </summary> /// <param name="path">Path to the assembly.</param> private string GetRuntimeVersion(string path) { FileState fileState = GetFileState(path); if (String.IsNullOrEmpty(fileState.RuntimeVersion)) { fileState.RuntimeVersion = getAssemblyRuntimeVersion(path); isDirty = true; } return(fileState.RuntimeVersion); }
/// <summary> /// Cached implementation of GetAssemblyName. /// </summary> /// <param name="path">The path to the file</param> /// <returns>The assembly name.</returns> private AssemblyNameExtension GetAssemblyName(string path) { // If the assembly is in an FX folder and its a well-known assembly // then we can short-circuit the File IO involved with GetAssemblyName() if (redistList != null) { string extension = Path.GetExtension(path); if (String.Compare(extension, ".dll", StringComparison.OrdinalIgnoreCase) == 0) { AssemblyEntry[] assemblyNames = redistList.FindAssemblyNameFromSimpleName ( Path.GetFileNameWithoutExtension(path) ); for (int i = 0; i < assemblyNames.Length; ++i) { string filename = Path.GetFileName(path); string pathFromRedistList = Path.Combine(assemblyNames[i].FrameworkDirectory, filename); if (String.Equals(path, pathFromRedistList, StringComparison.OrdinalIgnoreCase)) { return(new AssemblyNameExtension(assemblyNames[i].FullName)); } } } } // Not a well-known FX assembly so now check the cache. FileState fileState = GetFileState(path); if (fileState.Assembly == null) { fileState.Assembly = getAssemblyName(path); // Certain assemblies, like mscorlib may not have metadata. // Avoid continuously calling getAssemblyName on these files by // recording these as having an empty name. if (fileState.Assembly == null) { fileState.Assembly = AssemblyNameExtension.UnnamedAssembly; } isDirty = true; } if (fileState.Assembly.IsUnnamedAssembly) { return(null); } return(fileState.Assembly); }
private FileState GetFileState(string path) { FileState state = null; FileState state2 = (FileState)processWideFileStateCache[path]; FileState state3 = (FileState)this.instanceLocalFileStateCache[path]; if ((state2 == null) && (state3 != null)) { state = state3; processWideFileStateCache[path] = state3; } else if ((state2 != null) && (state3 == null)) { state = state2; this.instanceLocalFileStateCache[path] = state2; } else if ((state2 != null) && (state3 != null)) { if (state2.LastModified > state3.LastModified) { state = state2; this.instanceLocalFileStateCache[path] = state2; } else { state = state3; processWideFileStateCache[path] = state3; } } if (state == null) { state = new FileState { LastModified = this.getLastWriteTime(path) }; this.instanceLocalFileStateCache[path] = state; processWideFileStateCache[path] = state; this.isDirty = true; return(state); } if (this.getLastWriteTime(path) != state.LastModified) { state = new FileState { LastModified = this.getLastWriteTime(path) }; this.instanceLocalFileStateCache[path] = state; processWideFileStateCache[path] = state; this.isDirty = true; } return(state); }
/// <summary> /// Copy source to destination, unless SkipUnchangedFiles is true and they are equivalent. /// </summary> /// <returns>True if the file was copied or, on SkipUnchangedFiles, the file was equivalent.</returns> private bool DoCopyIfNecessary(FileState sourceFileState, FileState destinationFileState, CopyFileWithState copyFile) { bool success = true; try { if (SkipUnchangedFiles && IsMatchingSizeAndTimeStamp(sourceFileState, destinationFileState)) { // If we got here, then the file's time and size match AND // the user set the SkipUnchangedFiles flag which means we // should skip matching files. Log.LogMessageFromResources( MessageImportance.Low, "Copy.DidNotCopyBecauseOfFileMatch", sourceFileState.Name, destinationFileState.Name, "SkipUnchangedFiles", "true" ); } // We only do the cheap check for identicalness here, we try the more expensive check // of comparing the fullpaths of source and destination to see if they are identical, // in the exception handler lower down. else if (0 != String.Compare( sourceFileState.Name, destinationFileState.Name, StringComparison.OrdinalIgnoreCase)) { success = DoCopyWithRetries(sourceFileState, destinationFileState, copyFile); } } catch (OperationCanceledException) { success = false; } catch (PathTooLongException e) { Log.LogErrorWithCodeFromResources("Copy.Error", sourceFileState.Name, destinationFileState.Name, e.Message); success = false; } catch (Exception e) when(ExceptionHandling.IsIoRelatedException(e)) { Log.LogErrorWithCodeFromResources("Copy.Error", sourceFileState.Name, destinationFileState.Name, e.Message); success = false; } return(success); }
/// <summary> /// Ensure the read-only attribute on the specified file is off, so /// the file is writeable. /// </summary> private void MakeFileWriteable(FileState file, bool logActivity) { if (file.FileExists) { if (file.IsReadOnly) { if (logActivity) { Log.LogMessageFromResources(MessageImportance.Low, "Copy.RemovingReadOnlyAttribute", file.Name); } File.SetAttributes(file.Name, FileAttributes.Normal); file.Reset(); } } }
private FileState GetFileStateFromProcessWideCache(string path, FileState template) { // When reading from the process-wide cache, we always check to see if our data // is up-to-date to avoid getting stale data from a previous build. DateTime lastModified = getLastWriteTime(path); // Has another build seen this file before? FileState state; if (!s_processWideFileStateCache.TryGetValue(path, out state) || state.LastModified != lastModified) { // We've never seen it before, or we're out of date state = CreateFileState(lastModified, template); s_processWideFileStateCache[path] = state; } return(state); }
public void Exists() { string file = null; try { file = FileUtilities.GetTemporaryFile(); FileInfo info = new FileInfo(file); FileState state = new FileState(file); Assert.Equal(info.Exists, state.FileExists); } finally { File.Delete(file); } }
public void Name() { string file = null; try { file = FileUtilities.GetTemporaryFile(); FileInfo info = new FileInfo(file); FileState state = new FileState(file); Assert.AreEqual(info.FullName, state.Name); } finally { File.Delete(file); } }
private FileState GetFileState(string path) { // Looking up an assembly to get its metadata can be expensive for projects that reference large amounts // of assemblies. To avoid that expense, we remember and serialize this information betweeen runs in // XXXResolveAssemblyReferencesInput.cache files in the intermediate directory and also store it in an // process-wide cache to share between successive builds. // // To determine if this information is up-to-date, we use the last modified date of the assembly, however, // as calls to GetLastWriteTime can add up over hundreds and hundreds of files, we only check for // invalidation once per assembly per ResolveAssemblyReference session. FileState state = (FileState)upToDateLocalFileStateCache[path]; if (state == null) { // We haven't seen this file this ResolveAssemblyReference session state = ComputeFileStateFromCachesAndDisk(path); upToDateLocalFileStateCache[path] = state; } return(state); }
/// <summary> /// Method compares two files and returns true if their size and timestamp are identical. /// </summary> /// <param name="sourceFile">The source file</param> /// <param name="destinationFile">The destination file</param> private static bool IsMatchingSizeAndTimeStamp ( FileState sourceFile, FileState destinationFile ) { // If the destination doesn't exist, then it is not a matching file. if (!destinationFile.FileExists) { return(false); } if (sourceFile.LastWriteTimeUtcFast != destinationFile.LastWriteTimeUtcFast) { return(false); } if (sourceFile.Length != destinationFile.Length) { return(false); } return(true); }
private void TryCopyViaLink(string linkComment, MessageImportance messageImportance, FileState sourceFileState, FileState destinationFileState, ref bool destinationFileExists, ref bool linkCreated, Func<string, string, bool> createLink) { // Do not log a fake command line as well, as it's superfluous, and also potentially expensive Log.LogMessageFromResources(MessageImportance.Normal, linkComment, sourceFileState.Name, destinationFileState.Name); if (!_overwriteReadOnlyFiles) { destinationFileExists = destinationFileState.FileExists; } // CreateHardLink and CreateSymbolicLink cannot overwrite an existing file or link // so we need to delete the existing entry before we create the hard or symbolic link. // We need to do a best-effort check to see if the files are the same // if they are the same then we won't delete, just in case they refer to the same // physical file on disk. // Since we'll fall back to a copy (below) this will fail and issue a correct // message in the case that the source and destination are in fact the same file. if (destinationFileExists && !IsMatchingSizeAndTimeStamp(sourceFileState, destinationFileState)) { FileUtilities.DeleteNoThrow(destinationFileState.Name); } linkCreated = createLink(sourceFileState.Name, destinationFileState.Name); if (!linkCreated) { int errorCode = Marshal.GetHRForLastWin32Error(); Exception linkException = Marshal.GetExceptionForHR(errorCode); // This is only a message since we don't want warnings when copying to network shares etc. Log.LogMessageFromResources(messageImportance, "Copy.RetryingAsFileCopy", sourceFileState.Name, destinationFileState.Name, linkException.Message); } }
public void DoesNotExistLength() { string file = Guid.NewGuid().ToString("N"); // presumably doesn't exist Helpers.VerifyAssertThrowsSameWay(delegate () { var x = new FileInfo(file).Length; }, delegate () { var x = new FileState(file).Length; }); }
public void DoesNotExistIsDirectory() { Assert.Throws<FileNotFoundException>(() => { string file = Guid.NewGuid().ToString("N"); // presumably doesn't exist var x = new FileState(file).IsDirectory; } ); }
public void LastWriteTimeUtcReset() { string file = null; try { file = FileUtilities.GetTemporaryFile(); FileInfo info = new FileInfo(file); FileState state = new FileState(file); Assert.Equal(info.LastWriteTimeUtc, state.LastWriteTimeUtcFast); var time = new DateTime(2111, 1, 1); info.LastWriteTime = time; Assert.NotEqual(time.ToUniversalTime(), state.LastWriteTimeUtcFast); state.Reset(); Assert.Equal(time.ToUniversalTime(), state.LastWriteTimeUtcFast); } finally { File.Delete(file); } }
public void ReadOnlyReset() { string file = null; try { file = FileUtilities.GetTemporaryFile(); FileInfo info = new FileInfo(file); FileState state = new FileState(file); Assert.Equal(info.IsReadOnly, state.IsReadOnly); info.IsReadOnly = !info.IsReadOnly; state.Reset(); Assert.Equal(true, state.IsReadOnly); } finally { (new FileInfo(file)).IsReadOnly = false; File.Delete(file); } }
public void AccessDenied() { string locked = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "system32\\notepad.exe"); FileInfo info = new FileInfo(locked); FileState state = new FileState(locked); Assert.Equal(info.Exists, state.FileExists); if (!info.Exists) { // meh, somewhere else return; } Assert.Equal(info.Length, state.Length); Assert.Equal(info.LastWriteTime, state.LastWriteTime); Assert.Equal(info.LastWriteTimeUtc, state.LastWriteTimeUtcFast); }
public void ReadOnly() { string file = null; try { file = FileUtilities.GetTemporaryFile(); FileInfo info = new FileInfo(file); FileState state = new FileState(file); Assert.Equal(info.IsReadOnly, state.IsReadOnly); } finally { File.Delete(file); } }
/* * Method: CopyFile * * Don't really copy the file, just count how many times this was called. */ internal bool? CopyFile(FileState source, FileState destination) { ++copyCount; return true; }
public void LastWriteTimeUtc() { string file = null; try { file = FileUtilities.GetTemporaryFile(); FileInfo info = new FileInfo(file); FileState state = new FileState(file); Assert.Equal(info.LastWriteTimeUtc, state.LastWriteTimeUtcFast); } finally { File.Delete(file); } }
public void BadChars() { var state = new FileState("|"); var time = state.LastWriteTime; }
public void DoesNotExistIsDirectory() { string file = Guid.NewGuid().ToString("N"); // presumably doesn't exist var x = new FileState(file).IsDirectory; }
public void ExistsReset() { string file = null; try { file = FileUtilities.GetTemporaryFile(); FileInfo info = new FileInfo(file); FileState state = new FileState(file); Assert.AreEqual(info.Exists, state.FileExists); File.Delete(file); Assert.AreEqual(true, state.FileExists); state.Reset(); Assert.AreEqual(false, state.FileExists); } finally { if (File.Exists(file)) { File.Delete(file); } } }
/// <summary> /// Copy source to destination, unless SkipUnchangedFiles is true and they are equivalent. /// </summary> /// <param name="sourceFile"></param> /// <param name="destinationFile"></param> /// <param name="copyFile"></param> /// <returns></returns> private bool DoCopyIfNecessary(FileState sourceFileState, FileState destinationFileState, CopyFileWithState copyFile) { bool success = true; try { if (_skipUnchangedFiles && IsMatchingSizeAndTimeStamp(sourceFileState, destinationFileState)) { // If we got here, then the file's time and size match AND // the user set the SkipUnchangedFiles flag which means we // should skip matching files. Log.LogMessageFromResources ( MessageImportance.Low, "Copy.DidNotCopyBecauseOfFileMatch", sourceFileState.Name, destinationFileState.Name, "SkipUnchangedFiles", "true" ); } // We only do the cheap check for identicalness here, we try the more expensive check // of comparing the fullpaths of source and destination to see if they are identical, // in the exception handler lower down. else if (0 != String.Compare(sourceFileState.Name, destinationFileState.Name, StringComparison.OrdinalIgnoreCase)) { success = DoCopyWithRetries(sourceFileState, destinationFileState, copyFile); } } catch (PathTooLongException e) { Log.LogErrorWithCodeFromResources("Copy.Error", sourceFileState.Name, destinationFileState.Name, e.Message); success = false; } catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) { Log.LogErrorWithCodeFromResources("Copy.Error", sourceFileState.Name, destinationFileState.Name, e.Message); success = false; } return success; }
/// <summary> /// Copy one file with the appropriate number of retries if it fails. /// </summary> private bool DoCopyWithRetries(FileState sourceFileState, FileState destinationFileState, CopyFileWithState copyFile) { bool? result = null; int retries = 0; while (true && !_canceling) { try { result = copyFile(sourceFileState, destinationFileState); if (result.HasValue) { return result.Value; } } catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) { if (e is ArgumentException || // Invalid chars e is NotSupportedException || // Colon in the middle of the path e is PathTooLongException) { // No use retrying these cases throw; } if (e is UnauthorizedAccessException || e is IOException) // Not clear why we can get one and not the other { int code = Marshal.GetHRForException(e); LogDiagnostic("Got {0} copying {1} to {2} and HR is {3}", e.ToString(), sourceFileState.Name, destinationFileState.Name, code); if (code == Microsoft.Build.Tasks.NativeMethods.ERROR_ACCESS_DENIED) { // ERROR_ACCESS_DENIED can either mean there's an ACL preventing us, or the file has the readonly bit set. // In either case, that's likely not a race, and retrying won't help. // Retrying is mainly for ERROR_SHARING_VIOLATION, where someone else is using the file right now. // However, there is a limited set of circumstances where a copy failure will show up as access denied due // to a failure to reset the readonly bit properly, in which case retrying will succeed. This seems to be // a pretty edge scenario, but since some of our internal builds appear to be hitting it, provide a secret // environment variable to allow overriding the default behavior and forcing retries in this circumstance as well. if (!s_alwaysRetryCopy) { throw; } else { LogDiagnostic("Retrying on ERROR_ACCESS_DENIED because MSBUILDALWAYSRETRY = 1"); } } } if (e is IOException && DestinationFolder != null && File.Exists(DestinationFolder.ItemSpec)) { // We failed to create the DestinationFolder because it's an existing file. No sense retrying. // We don't check for this case upstream because it'd be another hit to the filesystem. throw; } if (e is IOException) { // if this was just because the source and destination files are the // same file, that's not a failure. // Note -- we check this exceptional case here, not before the copy, for perf. if (PathsAreIdentical(sourceFileState.Name, destinationFileState.Name)) { return true; } } if (retries < Retries) { retries++; Log.LogWarningWithCodeFromResources("Copy.Retrying", sourceFileState.Name, destinationFileState.Name, retries, RetryDelayMilliseconds, e.Message); // if we have to retry for some reason, wipe the state -- it may not be correct anymore. destinationFileState.Reset(); Thread.Sleep(RetryDelayMilliseconds); continue; } else if (Retries > 0) { // Exception message is logged in caller Log.LogErrorWithCodeFromResources("Copy.ExceededRetries", sourceFileState.Name, destinationFileState.Name, Retries); throw; } else { throw; } } if (retries < Retries) { retries++; Log.LogWarningWithCodeFromResources("Copy.Retrying", sourceFileState.Name, destinationFileState.Name, retries, RetryDelayMilliseconds, String.Empty /* no details */); // if we have to retry for some reason, wipe the state -- it may not be correct anymore. destinationFileState.Reset(); Thread.Sleep(RetryDelayMilliseconds); } else if (Retries > 0) { Log.LogErrorWithCodeFromResources("Copy.ExceededRetries", sourceFileState.Name, destinationFileState.Name, Retries); return false; } else { return false; } } // Canceling return false; }
public void IsDirectoryTrue() { var state = new FileState(Path.GetTempPath()); Assert.Equal(true, state.IsDirectory); }
/// <summary> /// Method compares two files and returns true if their size and timestamp are identical. /// </summary> /// <param name="sourceFile">The source file</param> /// <param name="destinationFile">The destination file</param> /// <returns></returns> private static bool IsMatchingSizeAndTimeStamp ( FileState sourceFile, FileState destinationFile ) { // If the destination doesn't exists, then it is not a matching file. if (!destinationFile.FileExists) { return false; } if (sourceFile.LastWriteTimeUtcFast != destinationFile.LastWriteTimeUtcFast) { return false; } if (sourceFile.Length != destinationFile.Length) { return false; } return true; }
public void Length() { string file = null; try { file = FileUtilities.GetTemporaryFile(); FileInfo info = new FileInfo(file); FileState state = new FileState(file); Assert.Equal(info.Length, state.Length); } finally { File.Delete(file); } }
private void TryCopyViaLink(string linkComment, MessageImportance messageImportance, FileState sourceFileState, FileState destinationFileState, ref bool destinationFileExists, out bool linkCreated, ref string errorMessage, Func <string, string, string, bool> createLink) { // Do not log a fake command line as well, as it's superfluous, and also potentially expensive Log.LogMessageFromResources(MessageImportance.Normal, linkComment, sourceFileState.Name, destinationFileState.Name); if (!OverwriteReadOnlyFiles) { destinationFileExists = destinationFileState.FileExists; } // CreateHardLink and CreateSymbolicLink cannot overwrite an existing file or link // so we need to delete the existing entry before we create the hard or symbolic link. if (destinationFileExists) { FileUtilities.DeleteNoThrow(destinationFileState.Name); } linkCreated = createLink(sourceFileState.Name, destinationFileState.Name, errorMessage); if (!linkCreated) { // This is only a message since we don't want warnings when copying to network shares etc. Log.LogMessageFromResources(messageImportance, "Copy.RetryingAsFileCopy", sourceFileState.Name, destinationFileState.Name, errorMessage); } }
public void LengthHuge() { var bigFile = @"d:\proj\hugefile"; //var dummy = new string('x', 10000000); //using (StreamWriter w = new StreamWriter(bigFile)) //{ // for (int i = 0; i < 450; i++) // { // w.Write(dummy); // } //} Console.WriteLine((new FileState(bigFile)).Length); FileInfo info = new FileInfo(bigFile); FileState state = new FileState(bigFile); Assert.AreEqual(info.Exists, state.Exists); if (!info.Exists) { // meh, somewhere else return; } Assert.AreEqual(info.Length, state.Length); }
/// <summary> /// Copy one file from source to destination. Create the target directory if necessary and /// leave the file read-write. /// </summary> /// <returns>Return true to indicate success, return false to indicate failure and NO retry, return NULL to indicate retry.</returns> private bool?CopyFileWithLogging ( FileState sourceFileState, // The source file FileState destinationFileState // The destination file ) { bool destinationFileExists = false; if (destinationFileState.DirectoryExists) { Log.LogErrorWithCodeFromResources("Copy.DestinationIsDirectory", sourceFileState.Name, destinationFileState.Name); return(false); } if (sourceFileState.DirectoryExists) { // If the source file passed in is actually a directory instead of a file, log a nice // error telling the user so. Otherwise, .NET Framework's File.Copy method will throw // an UnauthorizedAccessException saying "access is denied", which is not very useful // to the user. Log.LogErrorWithCodeFromResources("Copy.SourceIsDirectory", sourceFileState.Name); return(false); } if (!sourceFileState.FileExists) { Log.LogErrorWithCodeFromResources("Copy.SourceFileNotFound", sourceFileState.Name); return(false); } string destinationFolder = Path.GetDirectoryName(destinationFileState.Name); if (!string.IsNullOrEmpty(destinationFolder) && !_directoriesKnownToExist.ContainsKey(destinationFolder)) { if (!FileSystems.Default.DirectoryExists(destinationFolder)) { Log.LogMessageFromResources(MessageImportance.Normal, "Copy.CreatesDirectory", destinationFolder); Directory.CreateDirectory(destinationFolder); } // It's very common for a lot of files to be copied to the same folder. // Eg., "c:\foo\a"->"c:\bar\a", "c:\foo\b"->"c:\bar\b" and so forth. // We don't want to check whether this folder exists for every single file we copy. So store which we've checked. _directoriesKnownToExist.TryAdd(destinationFolder, true); } if (OverwriteReadOnlyFiles) { MakeFileWriteable(destinationFileState, true); destinationFileExists = destinationFileState.FileExists; } bool linkCreated = false; string errorMessage = string.Empty; // If we want to create hard or symbolic links, then try that first if (UseHardlinksIfPossible) { TryCopyViaLink("Copy.HardLinkComment", MessageImportance.Normal, sourceFileState, destinationFileState, ref destinationFileExists, out linkCreated, ref errorMessage, (source, destination, errMessage) => NativeMethods.MakeHardLink(destination, source, ref errorMessage)); } else if (UseSymboliclinksIfPossible) { TryCopyViaLink("Copy.SymbolicLinkComment", MessageImportance.Normal, sourceFileState, destinationFileState, ref destinationFileExists, out linkCreated, ref errorMessage, (source, destination, errMessage) => NativeMethods.MakeSymbolicLink(destination, source, ref errorMessage)); } // If the link was not created (either because the user didn't want one, or because it couldn't be created) // then let's copy the file if (!linkCreated) { // Do not log a fake command line as well, as it's superfluous, and also potentially expensive string sourceFilePath = FileUtilities.GetFullPathNoThrow(sourceFileState.Name); string destinationFilePath = FileUtilities.GetFullPathNoThrow(destinationFileState.Name); Log.LogMessageFromResources(MessageImportance.Normal, "Copy.FileComment", sourceFilePath, destinationFilePath); File.Copy(sourceFileState.Name, destinationFileState.Name, true); } destinationFileState.Reset(); // If the destinationFile file exists, then make sure it's read-write. // The File.Copy command copies attributes, but our copy needs to // leave the file writeable. if (sourceFileState.IsReadOnly) { MakeFileWriteable(destinationFileState, false); } return(true); }
public void NameReset() { string file = null; try { file = FileUtilities.GetTemporaryFile(); FileInfo info = new FileInfo(file); FileState state = new FileState(file); Assert.Equal(info.FullName, state.Name); string originalName = info.FullName; string oldFile = file; file = oldFile + "2"; File.Move(oldFile, file); Assert.Equal(originalName, state.Name); state.Reset(); Assert.Equal(originalName, state.Name); // Name is from the constructor, didn't change } finally { File.Delete(file); } }
private void TryCopyViaLink(string linkComment, MessageImportance messageImportance, FileState sourceFileState, FileState destinationFileState, ref bool destinationFileExists, out bool linkCreated, ref string errorMessage, Func <string, string, string, bool> createLink) { // Do not log a fake command line as well, as it's superfluous, and also potentially expensive Log.LogMessageFromResources(MessageImportance.Normal, linkComment, sourceFileState.Name, destinationFileState.Name); if (!OverwriteReadOnlyFiles) { destinationFileExists = destinationFileState.FileExists; } // CreateHardLink and CreateSymbolicLink cannot overwrite an existing file or link // so we need to delete the existing entry before we create the hard or symbolic link. // We need to do a best-effort check to see if the files are the same // if they are the same then we won't delete, just in case they refer to the same // physical file on disk. // Since we'll fall back to a copy (below) this will fail and issue a correct // message in the case that the source and destination are in fact the same file. if (destinationFileExists && !IsMatchingSizeAndTimeStamp(sourceFileState, destinationFileState)) { FileUtilities.DeleteNoThrow(destinationFileState.Name); } linkCreated = createLink(sourceFileState.Name, destinationFileState.Name, errorMessage); if (!linkCreated) { // This is only a message since we don't want warnings when copying to network shares etc. Log.LogMessageFromResources(messageImportance, "Copy.RetryingAsFileCopy", sourceFileState.Name, destinationFileState.Name, errorMessage); } }
public void LengthReset() { string file = null; try { file = FileUtilities.GetTemporaryFile(); FileInfo info = new FileInfo(file); FileState state = new FileState(file); Assert.Equal(info.Length, state.Length); File.WriteAllText(file, "x"); Assert.Equal(info.Length, state.Length); state.Reset(); info.Refresh(); Assert.Equal(info.Length, state.Length); } finally { File.Delete(file); } }
/// <summary> /// Copy one file from source to destination. Create the target directory if necessary and /// leave the file read-write. /// </summary> /// <param name="sourceFile"></param> /// <param name="destinationFile"></param> /// <returns>Return true to indicate success, return false to indicate failure and NO retry, return NULL to indicate retry.</returns> private bool? CopyFileWithLogging ( FileState sourceFileState, // The source file FileState destinationFileState // The destination file ) { bool destinationFileExists = false; if (destinationFileState.DirectoryExists) { Log.LogErrorWithCodeFromResources("Copy.DestinationIsDirectory", sourceFileState.Name, destinationFileState.Name); return false; } if (sourceFileState.DirectoryExists) { // If the source file passed in is actually a directory instead of a file, log a nice // error telling the user so. Otherwise, .NET Framework's File.Copy method will throw // an UnauthorizedAccessException saying "access is denied", which is not very useful // to the user. Log.LogErrorWithCodeFromResources("Copy.SourceIsDirectory", sourceFileState.Name); return false; } if (!sourceFileState.FileExists) { Log.LogErrorWithCodeFromResources("Copy.SourceFileNotFound", sourceFileState.Name); return false; } string destinationFolder = Path.GetDirectoryName(destinationFileState.Name); if (destinationFolder != null && destinationFolder.Length > 0 && !_directoriesKnownToExist.Contains(destinationFolder)) { if (!Directory.Exists(destinationFolder)) { Log.LogMessageFromResources(MessageImportance.Normal, "Copy.CreatesDirectory", destinationFolder); Directory.CreateDirectory(destinationFolder); } // It's very common for a lot of files to be copied to the same folder. // Eg., "c:\foo\a"->"c:\bar\a", "c:\foo\b"->"c:\bar\b" and so forth. // We don't want to check whether this folder exists for every single file we copy. So store which we've checked. _directoriesKnownToExist.Add(destinationFolder); } if (_overwriteReadOnlyFiles) { MakeFileWriteable(destinationFileState, true); destinationFileExists = destinationFileState.FileExists; } bool hardLinkCreated = false; // If we want to create hard links, then try that first if (UseHardlinksIfPossible) { // Do not log a fake command line as well, as it's superfluous, and also potentially expensive Log.LogMessageFromResources(MessageImportance.Normal, "Copy.HardLinkComment", sourceFileState.Name, destinationFileState.Name); if (!_overwriteReadOnlyFiles) { destinationFileExists = destinationFileState.FileExists; } // CreateHardLink cannot overwrite an existing file or hard link // so we need to delete the existing entry before we create the hard link. // We need to do a best-effort check to see if the files are the same // if they are the same then we won't delete, just in case they refer to the same // physical file on disk. // Since we'll fall back to a copy (below) this will fail and issue a correct // message in the case that the source and destination are in fact the same file. if (destinationFileExists && !IsMatchingSizeAndTimeStamp(sourceFileState, destinationFileState)) { FileUtilities.DeleteNoThrow(destinationFileState.Name); } hardLinkCreated = NativeMethods.CreateHardLink(destinationFileState.Name, sourceFileState.Name, IntPtr.Zero /* reserved, must be NULL */); if (!hardLinkCreated) { int errorCode = Marshal.GetHRForLastWin32Error(); Exception hardLinkException = Marshal.GetExceptionForHR(errorCode); // This is only a message since we don't want warnings when copying to network shares etc. Log.LogMessageFromResources(MessageImportance.Low, "Copy.RetryingAsFileCopy", sourceFileState.Name, destinationFileState.Name, hardLinkException.Message); } } // If the hard link was not created (either because the user didn't want one, or because it couldn't be created) // then let's copy the file if (!hardLinkCreated) { // Do not log a fake command line as well, as it's superfluous, and also potentially expensive Log.LogMessageFromResources(MessageImportance.Normal, "Copy.FileComment", sourceFileState.Name, destinationFileState.Name); File.Copy(sourceFileState.Name, destinationFileState.Name, true); } destinationFileState.Reset(); // If the destinationFile file exists, then make sure it's read-write. // The File.Copy command copies attributes, but our copy needs to // leave the file writeable. if (sourceFileState.IsReadOnly) { MakeFileWriteable(destinationFileState, false); } return true; }
public void LengthOnDirectory() { Helpers.VerifyAssertThrowsSameWay(delegate () { var x = new FileInfo(Path.GetTempPath()).Length; }, delegate () { var x = new FileState(Path.GetTempPath()).Length; }); }
/// <summary> /// Copy one file from source to destination. Create the target directory if necessary and /// leave the file read-write. /// </summary> /// <param name="sourceFile"></param> /// <param name="destinationFile"></param> /// <returns>Return true to indicate success, return false to indicate failure and NO retry, return NULL to indicate retry.</returns> private bool?CopyFileWithLogging ( FileState sourceFileState, // The source file FileState destinationFileState // The destination file ) { bool destinationFileExists = false; if (destinationFileState.DirectoryExists) { Log.LogErrorWithCodeFromResources("Copy.DestinationIsDirectory", sourceFileState.Name, destinationFileState.Name); return(false); } if (sourceFileState.DirectoryExists) { // If the source file passed in is actually a directory instead of a file, log a nice // error telling the user so. Otherwise, .NET Framework's File.Copy method will throw // an UnauthorizedAccessException saying "access is denied", which is not very useful // to the user. Log.LogErrorWithCodeFromResources("Copy.SourceIsDirectory", sourceFileState.Name); return(false); } if (!sourceFileState.FileExists) { Log.LogErrorWithCodeFromResources("Copy.SourceFileNotFound", sourceFileState.Name); return(false); } string destinationFolder = Path.GetDirectoryName(destinationFileState.Name); if (destinationFolder != null && destinationFolder.Length > 0 && !_directoriesKnownToExist.Contains(destinationFolder)) { if (!Directory.Exists(destinationFolder)) { Log.LogMessageFromResources(MessageImportance.Normal, "Copy.CreatesDirectory", destinationFolder); Directory.CreateDirectory(destinationFolder); } // It's very common for a lot of files to be copied to the same folder. // Eg., "c:\foo\a"->"c:\bar\a", "c:\foo\b"->"c:\bar\b" and so forth. // We don't want to check whether this folder exists for every single file we copy. So store which we've checked. _directoriesKnownToExist.Add(destinationFolder); } if (_overwriteReadOnlyFiles) { MakeFileWriteable(destinationFileState, true); destinationFileExists = destinationFileState.FileExists; } bool hardLinkCreated = false; // If we want to create hard links, then try that first if (UseHardlinksIfPossible) { // Do not log a fake command line as well, as it's superfluous, and also potentially expensive Log.LogMessageFromResources(MessageImportance.Normal, "Copy.HardLinkComment", sourceFileState.Name, destinationFileState.Name); if (!_overwriteReadOnlyFiles) { destinationFileExists = destinationFileState.FileExists; } // CreateHardLink cannot overwrite an existing file or hard link // so we need to delete the existing entry before we create the hard link. // We need to do a best-effort check to see if the files are the same // if they are the same then we won't delete, just in case they refer to the same // physical file on disk. // Since we'll fall back to a copy (below) this will fail and issue a correct // message in the case that the source and destination are in fact the same file. if (destinationFileExists && !IsMatchingSizeAndTimeStamp(sourceFileState, destinationFileState)) { FileUtilities.DeleteNoThrow(destinationFileState.Name); } hardLinkCreated = NativeMethods.CreateHardLink(destinationFileState.Name, sourceFileState.Name, IntPtr.Zero /* reserved, must be NULL */); if (!hardLinkCreated) { int errorCode = Marshal.GetHRForLastWin32Error(); Exception hardLinkException = Marshal.GetExceptionForHR(errorCode); // This is only a message since we don't want warnings when copying to network shares etc. Log.LogMessageFromResources(MessageImportance.Low, "Copy.RetryingAsFileCopy", sourceFileState.Name, destinationFileState.Name, hardLinkException.Message); } } // If the hard link was not created (either because the user didn't want one, or because it couldn't be created) // then let's copy the file if (!hardLinkCreated) { // Do not log a fake command line as well, as it's superfluous, and also potentially expensive Log.LogMessageFromResources(MessageImportance.Normal, "Copy.FileComment", sourceFileState.Name, destinationFileState.Name); File.Copy(sourceFileState.Name, destinationFileState.Name, true); } destinationFileState.Reset(); // If the destinationFile file exists, then make sure it's read-write. // The File.Copy command copies attributes, but our copy needs to // leave the file writeable. if (sourceFileState.IsReadOnly) { MakeFileWriteable(destinationFileState, false); } return(true); }
/// <summary> /// Retrieve the file state object for this path. Create if necessary. /// </summary> /// <param name="path">The name of the file.</param> /// <returns>The file state object.</returns> private FileState GetFileState(string path) { // Is it in the process-wide cache? FileState cacheFileState = null; FileState processFileState = null; SystemState.s_processWideFileStateCache.TryGetValue(path, out processFileState); FileState instanceLocalFileState = instanceLocalFileState = (FileState)_instanceLocalFileStateCache[path]; // Sync the caches. if (processFileState == null && instanceLocalFileState != null) { cacheFileState = instanceLocalFileState; SystemState.s_processWideFileStateCache.TryAdd(path, instanceLocalFileState); } else if (processFileState != null && instanceLocalFileState == null) { cacheFileState = processFileState; _instanceLocalFileStateCache[path] = processFileState; } else if (processFileState != null && instanceLocalFileState != null) { if (processFileState.LastModified > instanceLocalFileState.LastModified) { cacheFileState = processFileState; _instanceLocalFileStateCache[path] = processFileState; } else { cacheFileState = instanceLocalFileState; SystemState.s_processWideFileStateCache.TryAdd(path, instanceLocalFileState); } } // Still no--need to create. if (cacheFileState == null) // Or check time stamp { cacheFileState = new FileState(); cacheFileState.LastModified = _getLastWriteTime(path); _instanceLocalFileStateCache[path] = cacheFileState; SystemState.s_processWideFileStateCache.TryAdd(path, cacheFileState); _isDirty = true; } else { // If time stamps have changed, then purge. DateTime lastModified = _getLastWriteTime(path); if (lastModified != cacheFileState.LastModified) { cacheFileState = new FileState(); cacheFileState.LastModified = _getLastWriteTime(path); _instanceLocalFileStateCache[path] = cacheFileState; SystemState.s_processWideFileStateCache.TryAdd(path, cacheFileState); _isDirty = true; } } return cacheFileState; }
/// <summary> /// Copy one file with the appropriate number of retries if it fails. /// </summary> private bool DoCopyWithRetries(FileState sourceFileState, FileState destinationFileState, CopyFileWithState copyFile) { int retries = 0; while (!_cancellationTokenSource.IsCancellationRequested) { try { bool?result = copyFile(sourceFileState, destinationFileState); if (result.HasValue) { return(result.Value); } } catch (OperationCanceledException) { break; } catch (Exception e) when(ExceptionHandling.IsIoRelatedException(e)) { if (e is ArgumentException || // Invalid chars e is NotSupportedException || // Colon in the middle of the path e is PathTooLongException) { // No use retrying these cases throw; } if (e is UnauthorizedAccessException || e is IOException) // Not clear why we can get one and not the other { int code = Marshal.GetHRForException(e); LogDiagnostic("Got {0} copying {1} to {2} and HR is {3}", e.ToString(), sourceFileState.Name, destinationFileState.Name, code); if (code == NativeMethods.ERROR_ACCESS_DENIED) { // ERROR_ACCESS_DENIED can either mean there's an ACL preventing us, or the file has the readonly bit set. // In either case, that's likely not a race, and retrying won't help. // Retrying is mainly for ERROR_SHARING_VIOLATION, where someone else is using the file right now. // However, there is a limited set of circumstances where a copy failure will show up as access denied due // to a failure to reset the readonly bit properly, in which case retrying will succeed. This seems to be // a pretty edge scenario, but since some of our internal builds appear to be hitting it, provide a secret // environment variable to allow overriding the default behavior and forcing retries in this circumstance as well. if (!s_alwaysRetryCopy) { throw; } else { LogDiagnostic("Retrying on ERROR_ACCESS_DENIED because MSBUILDALWAYSRETRY = 1"); } } } if (e is IOException && DestinationFolder != null && FileSystems.Default.FileExists(DestinationFolder.ItemSpec)) { // We failed to create the DestinationFolder because it's an existing file. No sense retrying. // We don't check for this case upstream because it'd be another hit to the filesystem. throw; } if (e is IOException) { // if this was just because the source and destination files are the // same file, that's not a failure. // Note -- we check this exceptional case here, not before the copy, for perf. if (PathsAreIdentical(sourceFileState.Name, destinationFileState.Name)) { return(true); } } if (retries < Retries) { retries++; Log.LogWarningWithCodeFromResources("Copy.Retrying", sourceFileState.Name, destinationFileState.Name, retries, RetryDelayMilliseconds, e.Message, GetLockedFileMessage(destinationFileState.Name)); // if we have to retry for some reason, wipe the state -- it may not be correct anymore. destinationFileState.Reset(); Thread.Sleep(RetryDelayMilliseconds); continue; } else if (Retries > 0) { // Exception message is logged in caller Log.LogErrorWithCodeFromResources("Copy.ExceededRetries", sourceFileState.Name, destinationFileState.Name, Retries, GetLockedFileMessage(destinationFileState.Name)); throw; } else { throw; } } if (retries < Retries) { retries++; Log.LogWarningWithCodeFromResources("Copy.Retrying", sourceFileState.Name, destinationFileState.Name, retries, RetryDelayMilliseconds, String.Empty /* no details */, GetLockedFileMessage(destinationFileState.Name)); // if we have to retry for some reason, wipe the state -- it may not be correct anymore. destinationFileState.Reset(); Thread.Sleep(RetryDelayMilliseconds); } else if (Retries > 0) { Log.LogErrorWithCodeFromResources("Copy.ExceededRetries", sourceFileState.Name, destinationFileState.Name, Retries, GetLockedFileMessage(destinationFileState.Name)); return(false); } else { return(false); } } // Canceling return(false); }
public void BadTooLongLastWriteTime() { Helpers.VerifyAssertThrowsSameWay( delegate () { var x = new FileInfo(new String('x', 5000)).LastWriteTime; }, delegate () { var x = new FileState(new String('x', 5000)).LastWriteTime; }); }
/// <summary> /// Pretend to be File.Copy. /// </summary> internal bool? Copy(FileState source, FileState destination) { _tries++; // 2nd and subsequent copies always succeed if (_filesCopiedSuccessfully.Count > 0 || _countOfSuccess == _tries) { Console.WriteLine("Copied {0} to {1} OK", source, destination); _filesCopiedSuccessfully.Add(source); return true; } if (_throwOnFailure) { throw new IOException("oops"); } else { return null; } }
private FileState ComputeFileStateFromCachesAndDisk(string path) { // Is it in the process-wide cache? FileState cacheFileState = null; FileState processFileState = null; s_processWideFileStateCache.TryGetValue(path, out processFileState); FileState instanceLocalFileState = (FileState)instanceLocalFileStateCache[path]; // Sync the caches. if (processFileState == null && instanceLocalFileState != null) { cacheFileState = instanceLocalFileState; SystemState.s_processWideFileStateCache[path] = instanceLocalFileState; } else if (processFileState != null && instanceLocalFileState == null) { cacheFileState = processFileState; instanceLocalFileStateCache[path] = processFileState; } else if (processFileState != null && instanceLocalFileState != null) { if (processFileState.LastModified > instanceLocalFileState.LastModified) { cacheFileState = processFileState; instanceLocalFileStateCache[path] = processFileState; } else { cacheFileState = instanceLocalFileState; SystemState.s_processWideFileStateCache[path] = instanceLocalFileState; } } bool isLastModifiedCached = instanceLocalLastModifiedCache.TryGetValue(path, out DateTime lastModified); if (!isLastModifiedCached) { lastModified = getLastWriteTime(path); instanceLocalLastModifiedCache[path] = lastModified; } // Still no--need to create. if (cacheFileState == null) // Or check time stamp { cacheFileState = new FileState(lastModified); instanceLocalFileStateCache[path] = cacheFileState; SystemState.s_processWideFileStateCache[path] = cacheFileState; isDirty = true; } else { // If time stamps have changed, then purge. if (lastModified != cacheFileState.LastModified) { cacheFileState = new FileState(lastModified); instanceLocalFileStateCache[path] = cacheFileState; SystemState.s_processWideFileStateCache[path] = cacheFileState; isDirty = true; } } return(cacheFileState); }
/// <summary> /// Copy one file from source to destination. Create the target directory if necessary and /// leave the file read-write. /// </summary> /// <param name="sourceFile"></param> /// <param name="destinationFile"></param> /// <returns>Return true to indicate success, return false to indicate failure and NO retry, return NULL to indicate retry.</returns> private bool? CopyFileWithLogging ( FileState sourceFileState, // The source file FileState destinationFileState // The destination file ) { bool destinationFileExists = false; if (destinationFileState.DirectoryExists) { Log.LogErrorWithCodeFromResources("Copy.DestinationIsDirectory", sourceFileState.Name, destinationFileState.Name); return false; } if (sourceFileState.DirectoryExists) { // If the source file passed in is actually a directory instead of a file, log a nice // error telling the user so. Otherwise, .NET Framework's File.Copy method will throw // an UnauthorizedAccessException saying "access is denied", which is not very useful // to the user. Log.LogErrorWithCodeFromResources("Copy.SourceIsDirectory", sourceFileState.Name); return false; } if (!sourceFileState.FileExists) { Log.LogErrorWithCodeFromResources("Copy.SourceFileNotFound", sourceFileState.Name); return false; } string destinationFolder = Path.GetDirectoryName(destinationFileState.Name); if (destinationFolder != null && destinationFolder.Length > 0 && !_directoriesKnownToExist.Contains(destinationFolder)) { if (!Directory.Exists(destinationFolder)) { Log.LogMessageFromResources(MessageImportance.Normal, "Copy.CreatesDirectory", destinationFolder); Directory.CreateDirectory(destinationFolder); } // It's very common for a lot of files to be copied to the same folder. // Eg., "c:\foo\a"->"c:\bar\a", "c:\foo\b"->"c:\bar\b" and so forth. // We don't want to check whether this folder exists for every single file we copy. So store which we've checked. _directoriesKnownToExist.Add(destinationFolder); } if (_overwriteReadOnlyFiles) { MakeFileWriteable(destinationFileState, true); destinationFileExists = destinationFileState.FileExists; } bool linkCreated = false; // If we want to create hard or symbolic links, then try that first if (UseHardlinksIfPossible) TryCopyViaLink("Copy.HardLinkComment", MessageImportance.High, sourceFileState, destinationFileState, ref destinationFileExists, ref linkCreated, (source, destination) => NativeMethods.CreateHardLink(destination, source, IntPtr.Zero /* reserved, must be NULL */)); else if (UseSymboliclinksIfPossible) TryCopyViaLink("Copy.SymbolicLinkComment", MessageImportance.High, sourceFileState, destinationFileState, ref destinationFileExists, ref linkCreated, (source, destination) => NativeMethods.CreateSymbolicLink(destination, source, SymbolicLink.File)); // If the link was not created (either because the user didn't want one, or because it couldn't be created) // then let's copy the file if (!linkCreated) { // Do not log a fake command line as well, as it's superfluous, and also potentially expensive Log.LogMessageFromResources(MessageImportance.Normal, "Copy.FileComment", sourceFileState.Name, destinationFileState.Name); File.Copy(sourceFileState.Name, destinationFileState.Name, true); } destinationFileState.Reset(); // If the destinationFile file exists, then make sure it's read-write. // The File.Copy command copies attributes, but our copy needs to // leave the file writeable. if (sourceFileState.IsReadOnly) { MakeFileWriteable(destinationFileState, false); } return true; }