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