1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 /** 6 This is meant to contain useful utilities for IO related work 7 **/ 8 9 #define TRACE 10 #define DEBUG 11 using System; 12 using System.IO; 13 using System.Text; 14 using System.Diagnostics; 15 using System.Collections.Generic; 16 using System.Threading.Tasks; 17 using System.Runtime.InteropServices; 18 19 //machine information 20 public static class FileSystemDebugInfo 21 { MachineInfo()22 public static String MachineInfo() 23 { 24 StringBuilder builder = new StringBuilder(String.Format("{0}/////////Machine Info///////////{0}", Environment.NewLine)); 25 builder.AppendLine(String.Format("CurrentDrive NTFS?: {0}", IsCurrentDriveNTFS())); 26 builder.AppendLine(String.Format("////////////////////{0}", Environment.NewLine)); 27 28 return builder.ToString(); 29 } 30 IsCurrentDriveNTFS()31 public static bool IsCurrentDriveNTFS() 32 { 33 return IOServices.IsDriveNTFS(IOServices.GetCurrentDrive()); 34 } 35 IsPathAdminAccessOnly(String path, bool treatPathAsFilePath)36 public static bool IsPathAdminAccessOnly(String path, bool treatPathAsFilePath) 37 { 38 //we leave invalid paths as valid testcase scenarios and don't filter these 39 //1) We check if the path is root on a system drive 40 //2) @TODO WinDir? 41 String systemDrive = Environment.GetEnvironmentVariable("SystemDrive"); 42 Char systemDriveLetter = systemDrive.ToLower()[0]; 43 try 44 { 45 String dirName = Path.GetFullPath(path); 46 if (treatPathAsFilePath) 47 dirName = Path.GetDirectoryName(dirName); 48 if ((new DirectoryInfo(dirName)).Parent == null) 49 { 50 if (Path.GetPathRoot(dirName).StartsWith(systemDriveLetter.ToString(), StringComparison.OrdinalIgnoreCase)) 51 return true; 52 } 53 } 54 catch (Exception) { } 55 return false; 56 } 57 } 58 59 /// <summary> 60 /// Due to the increasing number of context indexing services (mssearch.exe, etrust) operating in our test run machines, Directory operations like Delete and Move 61 /// are not guaranteed to work in first attempt. This utility class do these operations in a fail safe manner 62 /// Possible solutions 63 /// - Set FileAttributes.NotContentIndex on the directory. But there is a race between creating the directory and setting this property. Other than using ACL, can't see a good solution 64 /// - encourage labs to stop these services before a test run. This is under review by CLRLab but there are lots of other labs that do these too 65 /// - fail and retry attempt: which is what this class does 66 /// VSW 446086 and 473287 have more information on this. 67 /// </summary> 68 public static class FailSafeDirectoryOperations 69 { 70 /// <summary> 71 /// Deleting 72 /// </summary> 73 /// <param name="path"></param> 74 /// <param name="recursive"></param> 75 private const int MAX_ATTEMPT = 10; DeleteDirectory(String path, bool recursive)76 public static void DeleteDirectory(String path, bool recursive) 77 { 78 DeleteDirectoryInfo(new DirectoryInfo(path), recursive); 79 } 80 DeleteDirectoryInfo(DirectoryInfo dirInfo, bool recursive)81 public static void DeleteDirectoryInfo(DirectoryInfo dirInfo, bool recursive) 82 { 83 int dirModificationAttemptCount; 84 bool dirModificationOperationThrew; 85 dirModificationAttemptCount = 0; 86 do 87 { 88 dirModificationOperationThrew = false; 89 try 90 { 91 if (dirInfo.Exists) 92 dirInfo.Delete(recursive); 93 } 94 catch (IOException) 95 { 96 Console.Write("|"); 97 dirModificationOperationThrew = true; 98 } 99 catch (UnauthorizedAccessException) 100 { 101 Console.Write("}"); 102 dirModificationOperationThrew = true; 103 } 104 if (dirModificationOperationThrew) 105 { 106 Task.Delay(5000).Wait(); 107 } 108 } while (dirModificationOperationThrew && dirModificationAttemptCount++ < MAX_ATTEMPT); 109 EnsureDirectoryNotExist(dirInfo.FullName); 110 //We want to thrown if the operation failed 111 if (Directory.Exists(dirInfo.FullName)) 112 throw new ArgumentException("Throwing from FailSafeDirectoryOperations.DeleteDirectoryInfo. Delete unsuccessful"); 113 } 114 115 116 /// <summary> 117 /// Moving 118 /// </summary> 119 /// <param name="sourceName"></param> 120 /// <param name="destName"></param> MoveDirectory(String sourceName, String destName)121 public static void MoveDirectory(String sourceName, String destName) 122 { 123 MoveDirectoryInfo(new DirectoryInfo(sourceName), destName); 124 } 125 MoveDirectoryInfo(DirectoryInfo dirInfo, String dirToMove)126 public static DirectoryInfo MoveDirectoryInfo(DirectoryInfo dirInfo, String dirToMove) 127 { 128 int dirModificationAttemptCount; 129 bool dirModificationOperationThrew; 130 dirModificationAttemptCount = 0; 131 String originalDirName = dirInfo.FullName; 132 do 133 { 134 dirModificationOperationThrew = false; 135 try 136 { 137 dirInfo.MoveTo(dirToMove); 138 } 139 catch (IOException) 140 { 141 Console.Write(">"); 142 Task.Delay(5000).Wait(); 143 dirModificationOperationThrew = true; 144 } 145 } while (dirModificationOperationThrew && dirModificationAttemptCount++ < MAX_ATTEMPT); 146 EnsureDirectoryNotExist(originalDirName); 147 //We want to thrown if the operation failed 148 if (Directory.Exists(originalDirName)) 149 throw new ArgumentException("Throwing from FailSafeDirectoryOperations.MoveDirectory. Move unsuccessful"); 150 return dirInfo; 151 } 152 153 /// <summary> 154 /// It can take some time before the Directory.Exists will return false after a directory delete/Move 155 /// </summary> 156 /// <param name="path"></param> EnsureDirectoryNotExist(String path)157 private static void EnsureDirectoryNotExist(String path) 158 { 159 int dirModificationAttemptCount; 160 dirModificationAttemptCount = 0; 161 while (Directory.Exists(path) && dirModificationAttemptCount++ < MAX_ATTEMPT) 162 { 163 // This is because something like antivirus software or 164 // some disk indexing service has a handle to the directory. The directory 165 // won't be deleted until all of the handles are closed. 166 Task.Delay(5000).Wait(); 167 Console.Write("<"); 168 } 169 } 170 } 171 172 173 174 /// <summary> 175 /// This class is meant to create directory and files under it 176 /// </summary> 177 public class ManageFileSystem : IDisposable 178 { 179 private const int DefaultDirectoryDepth = 3; 180 private const int DefaultNumberofFiles = 100; 181 private const int MaxNumberOfSubDirsPerDir = 2; 182 //@TODO 183 public const String DirPrefixName = "Laks_"; 184 185 private int _directoryLevel; 186 private int _numberOfFiles; 187 private string _startDir; 188 189 private Random _random; 190 191 private List<String> _listOfFiles; 192 private List<String> _listOfAllDirs; 193 private Dictionary<int, Dictionary<String, List<String>>> _allDirs; 194 195 ManageFileSystem()196 public ManageFileSystem() 197 { 198 Init(GetNonExistingDir(Directory.GetCurrentDirectory(), DirPrefixName), DefaultDirectoryDepth, DefaultNumberofFiles); 199 } ManageFileSystem(String startDirName)200 public ManageFileSystem(String startDirName) 201 : this(startDirName, DefaultDirectoryDepth, DefaultNumberofFiles) 202 { 203 } ManageFileSystem(String startDirName, int dirDepth, int numFiles)204 public ManageFileSystem(String startDirName, int dirDepth, int numFiles) 205 { 206 Init(startDirName, dirDepth, numFiles); 207 } 208 GetNonExistingDir(String parentDir, String prefix)209 public static String GetNonExistingDir(String parentDir, String prefix) 210 { 211 String tempPath; 212 while (true) 213 { 214 tempPath = Path.Combine(parentDir, String.Format("{0}{1}", prefix, Path.GetFileNameWithoutExtension(Path.GetRandomFileName()))); 215 if (!Directory.Exists(tempPath) && !File.Exists(tempPath)) 216 break; 217 } 218 return tempPath; 219 } 220 Dispose()221 public void Dispose() 222 { 223 Dispose(true); 224 GC.SuppressFinalize(this); 225 } 226 Dispose(bool disposing)227 protected virtual void Dispose(bool disposing) 228 { 229 if (disposing) 230 { 231 // free other state (managed objects) 232 // set interesting (large) fields to null 233 } 234 // free your own state (unmanaged objects) 235 FailSafeDirectoryOperations.DeleteDirectory(_startDir, true); 236 } 237 ~ManageFileSystem()238 ~ManageFileSystem() 239 { 240 Dispose(false); 241 } 242 Init(String startDirName, int dirDepth, int numFiles)243 private void Init(String startDirName, int dirDepth, int numFiles) 244 { 245 if (Directory.Exists(Path.GetFullPath(startDirName))) 246 throw new ArgumentException(String.Format("ERROR: Directory exists : {0}", _startDir)); 247 _startDir = Path.GetFullPath(startDirName); 248 _directoryLevel = dirDepth; 249 _numberOfFiles = numFiles; 250 _random = new Random(-55); 251 CreateFileSystem(); 252 } 253 254 /// <summary> 255 /// This API creates a file system under m_startDir, m_DirectoryLevel deep with m_numberOfFiles 256 /// We will store the created information on collections so as to get to them later 257 /// </summary> CreateFileSystem()258 private void CreateFileSystem() 259 { 260 _listOfFiles = new List<String>(); 261 _listOfAllDirs = new List<String>(); 262 Directory.CreateDirectory(_startDir); 263 //we will not include this directory 264 // m_listOfAllDirs.Add(m_startDir); 265 266 String currentWorkingDir = _startDir; 267 String parentDir = _startDir; 268 269 _allDirs = new Dictionary<int, Dictionary<String, List<String>>>(); 270 // List<String> dirsForOneLevel = new List<String>(); 271 // List<String> tempDirsForOneLevel; 272 List<String> filesForThisDir; 273 Dictionary<String, List<String>> dirsForOneLevel = new Dictionary<String, List<String>>(); 274 Dictionary<String, List<String>> tempDirsForOneLevel; 275 dirsForOneLevel.Add(_startDir, new List<String>()); 276 _allDirs.Add(0, dirsForOneLevel); 277 278 //First we create the directories 279 for (int i = 0; i < (_directoryLevel - 1); i++) 280 { 281 dirsForOneLevel = _allDirs[i]; 282 int numOfDirForThisLevel = _random.Next((MaxNumberOfSubDirsPerDir + 1)); 283 int numOfDirPerDir = numOfDirForThisLevel / dirsForOneLevel.Count; 284 //@TODO!! we should handle this situation in a better way 285 if (numOfDirPerDir == 0) 286 numOfDirPerDir = 1; 287 // Trace.Assert(numOfDirPerDir > 0, "Err_897324g! @TODO handle this scenario"); 288 tempDirsForOneLevel = new Dictionary<String, List<String>>(); 289 foreach (String dir in dirsForOneLevel.Keys) 290 // for (int j = 0; j < dirsForOneLevel.Count; j++) 291 { 292 for (int k = 0; k < numOfDirPerDir; k++) 293 { 294 String dirName = GetNonExistingDir(dir, DirPrefixName); 295 Debug.Assert(!Directory.Exists(dirName), String.Format("ERR_93472g! Directory exists: {0}", dirName)); 296 tempDirsForOneLevel.Add(dirName, new List<String>()); 297 _listOfAllDirs.Add(dirName); 298 Directory.CreateDirectory(dirName); 299 } 300 } 301 _allDirs.Add(i + 1, tempDirsForOneLevel); 302 } 303 //Then we add the files 304 //@TODO!! random or fixed? 305 int numberOfFilePerDirLevel = _numberOfFiles / _directoryLevel; 306 Byte[] bits; 307 for (int i = 0; i < _directoryLevel; i++) 308 { 309 dirsForOneLevel = _allDirs[i]; 310 int numOfFilesForThisLevel = _random.Next(numberOfFilePerDirLevel + 1); 311 int numOFilesPerDir = numOfFilesForThisLevel / dirsForOneLevel.Count; 312 //UPDATE: 2/1/2005, we will add at least 1 313 if (numOFilesPerDir == 0) 314 numOFilesPerDir = 1; 315 // for (int j = 0; j < dirsForOneLevel.Count; j++) 316 foreach (String dir in dirsForOneLevel.Keys) 317 { 318 filesForThisDir = dirsForOneLevel[dir]; 319 for (int k = 0; k < numOFilesPerDir; k++) 320 { 321 String fileName = Path.Combine(dir, Path.GetFileName(Path.GetRandomFileName())); 322 bits = new Byte[_random.Next(10)]; 323 _random.NextBytes(bits); 324 File.WriteAllBytes(fileName, bits); 325 _listOfFiles.Add(fileName); 326 filesForThisDir.Add(fileName); 327 } 328 } 329 } 330 } 331 332 public String StartDirectory 333 { 334 get { return _startDir; } 335 } 336 337 //some methods to help us GetDirectories(int level)338 public String[] GetDirectories(int level) 339 { 340 Dictionary<String, List<String>> dirsForThisLevel = null; 341 if (_allDirs.TryGetValue(level, out dirsForThisLevel)) 342 { 343 // Dictionary<String, List<String>> dirsForThisLevel = m_allDirs[level]; 344 ICollection<String> keys = dirsForThisLevel.Keys; 345 String[] values = new String[keys.Count]; 346 keys.CopyTo(values, 0); 347 return values; 348 } 349 else 350 return new String[0]; 351 } 352 353 /// <summary> 354 /// Note that this doesn't return the m_startDir 355 /// </summary> 356 /// <returns></returns> GetAllDirectories()357 public String[] GetAllDirectories() 358 { 359 return _listOfAllDirs.ToArray(); 360 } 361 362 GetFiles(String dirName, int level)363 public String[] GetFiles(String dirName, int level) 364 { 365 String dirFullName = Path.GetFullPath(dirName); 366 Dictionary<String, List<String>> dirsForThisLevel = _allDirs[level]; 367 foreach (String dir in dirsForThisLevel.Keys) 368 { 369 if (dir.Equals(dirFullName, StringComparison.CurrentCultureIgnoreCase)) 370 return dirsForThisLevel[dir].ToArray(); 371 } 372 return null; 373 } 374 GetAllFiles()375 public String[] GetAllFiles() 376 { 377 return _listOfFiles.ToArray(); 378 } 379 } 380 381 382 public class TestInfo 383 { TestInfo()384 static TestInfo() 385 { 386 CurrentDirectory = Directory.GetCurrentDirectory(); 387 } 388 389 public static string CurrentDirectory { get; set; } 390 ExceptionCode()391 private delegate void ExceptionCode(); 392 DeleteFile(String fileName)393 private void DeleteFile(String fileName) 394 { 395 if (File.Exists(fileName)) 396 File.Delete(fileName); 397 } 398 399 400 //Checks for error Eval(bool expression, String msg, params Object[] values)401 private static bool Eval(bool expression, String msg, params Object[] values) 402 { 403 return Eval(expression, String.Format(msg, values)); 404 } 405 Eval(T actual, T expected, String errorMsg)406 private static bool Eval<T>(T actual, T expected, String errorMsg) 407 { 408 bool retValue = expected == null ? actual == null : expected.Equals(actual); 409 410 if (!retValue) 411 Eval(retValue, errorMsg + 412 " Expected:" + (null == expected ? "<null>" : expected.ToString()) + 413 " Actual:" + (null == actual ? "<null>" : actual.ToString())); 414 415 return retValue; 416 } 417 Eval(bool expression, String msg)418 private static bool Eval(bool expression, String msg) 419 { 420 if (!expression) 421 { 422 Console.WriteLine(msg); 423 } 424 return expression; 425 } 426 427 //Checks for a particular type of exception CheckException(ExceptionCode test, string error)428 private static void CheckException<E>(ExceptionCode test, string error) 429 { 430 CheckException<E>(test, error, null); 431 } 432 433 //Checks for a particular type of exception and an Exception msg CheckException(ExceptionCode test, string error, String msgExpected)434 private static void CheckException<E>(ExceptionCode test, string error, String msgExpected) 435 { 436 bool exception = false; 437 try 438 { 439 test(); 440 error = String.Format("{0} Exception NOT thrown ", error); 441 } 442 catch (Exception e) 443 { 444 if (e.GetType() == typeof(E)) 445 { 446 exception = true; 447 if (System.Globalization.CultureInfo.CurrentUICulture.Name == "en-US" && msgExpected != null && e.Message != msgExpected) 448 { 449 exception = false; 450 error = String.Format("{0} Message Different: <{1}>", error, e.Message); 451 } 452 } 453 else 454 error = String.Format("{0} Exception type: {1}", error, e.GetType().Name); 455 } 456 Eval(exception, error); 457 } 458 } 459