Moving BSTR conv to native code in SecureStringToBSTR.
[mono.git] / mcs / class / referencesource / mscorlib / system / io / directory.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  Directory
9 ** 
10 ** <OWNER>[....]</OWNER>
11 **
12 **
13 ** Purpose: Exposes routines for enumerating through a 
14 ** directory.
15 **
16 **          April 11,2000
17 **
18 ===========================================================*/
19
20 using System;
21 using System.Collections;
22 using System.Collections.Generic;
23 using System.Security;
24 using System.Security.Permissions;
25 using Microsoft.Win32;
26 using Microsoft.Win32.SafeHandles;
27 using System.Text;
28 using System.Runtime.InteropServices;
29 using System.Globalization;
30 using System.Runtime.Versioning;
31 using System.Diagnostics.Contracts;
32 using System.Threading;
33
34 #if FEATURE_MACL
35 using System.Security.AccessControl;
36 #endif
37
38 namespace System.IO {
39     [ComVisible(true)]
40     public static class Directory {
41         [ResourceExposure(ResourceScope.Machine)]
42         [ResourceConsumption(ResourceScope.Machine)]
43         public static DirectoryInfo GetParent(String path)
44         {
45             if (path==null)
46                 throw new ArgumentNullException("path");
47
48             if (path.Length==0)
49                 throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"), "path");
50             Contract.EndContractBlock();
51
52             String fullPath = Path.GetFullPathInternal(path);
53                     
54             String s = Path.GetDirectoryName(fullPath);
55             if (s==null)
56                  return null;
57             return new DirectoryInfo(s);
58         }
59
60         [System.Security.SecuritySafeCritical]
61         [ResourceExposure(ResourceScope.Machine)]
62         [ResourceConsumption(ResourceScope.Machine)]
63         public static DirectoryInfo CreateDirectory(String path) {
64             if (path == null)
65                 throw new ArgumentNullException("path");
66             if (path.Length == 0)
67                 throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"));
68             Contract.EndContractBlock();
69
70 #if FEATURE_LEGACYNETCF
71             if(CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
72                 System.Reflection.Assembly callingAssembly = System.Reflection.Assembly.GetCallingAssembly();
73                 if(callingAssembly != null && !callingAssembly.IsProfileAssembly) {
74                     string caller = new System.Diagnostics.StackFrame(1).GetMethod().FullName;
75                     string callee = System.Reflection.MethodBase.GetCurrentMethod().FullName;
76                     throw new MethodAccessException(String.Format(
77                         CultureInfo.CurrentCulture,
78                         Environment.GetResourceString("Arg_MethodAccessException_WithCaller"),
79                         caller,
80                         callee));
81                 }
82             }
83 #endif // FEATURE_LEGACYNETCF
84
85             return InternalCreateDirectoryHelper(path, true);
86         }
87
88         [System.Security.SecurityCritical]
89         [ResourceExposure(ResourceScope.Machine)]
90         [ResourceConsumption(ResourceScope.Machine)]
91         internal static DirectoryInfo UnsafeCreateDirectory(String path)
92         {
93             if (path == null)
94                 throw new ArgumentNullException("path");
95             if (path.Length == 0)
96                 throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"));
97             Contract.EndContractBlock();
98
99             return InternalCreateDirectoryHelper(path, false);
100         }
101
102         [System.Security.SecurityCritical] 
103         [ResourceExposure(ResourceScope.Machine)]
104         [ResourceConsumption(ResourceScope.Machine)]
105         internal static DirectoryInfo InternalCreateDirectoryHelper(String path, bool checkHost)
106         {
107             Contract.Requires(path != null);
108             Contract.Requires(path.Length != 0);
109
110             String fullPath = Path.GetFullPathInternal(path);
111
112             // You need read access to the directory to be returned back and write access to all the directories 
113             // that you need to create. If we fail any security checks we will not create any directories at all.
114             // We attempt to create directories only after all the security checks have passed. This is avoid doing
115             // a demand at every level.
116             String demandDir = GetDemandDir(fullPath, true);
117
118 #if FEATURE_CORECLR
119             if (checkHost)
120             {
121                 FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, path, demandDir);
122                 state.EnsureState(); // do the check on the AppDomainManager to make sure this is allowed  
123             }
124 #else
125             FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandDir, false, false);
126 #endif
127
128             InternalCreateDirectory(fullPath, path, null, checkHost);
129
130             return new DirectoryInfo(fullPath, false);
131         }
132
133 #if FEATURE_MACL
134         [System.Security.SecuritySafeCritical]  // auto-generated
135         [ResourceExposure(ResourceScope.Machine)]
136         [ResourceConsumption(ResourceScope.Machine)]
137         public static DirectoryInfo CreateDirectory(String path, DirectorySecurity directorySecurity) {
138             if (path==null)
139                 throw new ArgumentNullException("path");
140             if (path.Length == 0)
141                 throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"));
142             Contract.EndContractBlock();
143             
144             String fullPath = Path.GetFullPathInternal(path);
145
146             // You need read access to the directory to be returned back and write access to all the directories 
147             // that you need to create. If we fail any security checks we will not create any directories at all.
148             // We attempt to create directories only after all the security checks have passed. This is avoid doing
149             // a demand at every level.
150             String demandDir = GetDemandDir(fullPath, true); 
151             FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandDir, false, false );
152
153             InternalCreateDirectory(fullPath, path, directorySecurity);
154             
155             return new DirectoryInfo(fullPath, false);
156         }
157 #endif // FEATURE_MACL
158
159         // Input to this method should already be fullpath. This method will ensure that we append 
160         // the trailing slash only when appropriate and when thisDirOnly is specified append a "." 
161         // at the end of the path to indicate that the demand is only for the fullpath and not 
162         // everything underneath it.
163         [ResourceExposure(ResourceScope.None)]
164         [ResourceConsumption(ResourceScope.None, ResourceScope.None)]
165         internal static String GetDemandDir(string fullPath, bool thisDirOnly)
166         {
167             String demandPath;
168
169             if (thisDirOnly) {
170                 if (fullPath.EndsWith( Path.DirectorySeparatorChar ) 
171                     || fullPath.EndsWith( Path.AltDirectorySeparatorChar ) )
172                     demandPath = fullPath + ".";
173                 else
174                     demandPath = fullPath + Path.DirectorySeparatorCharAsString + ".";
175             }
176             else {
177                 if (!(fullPath.EndsWith( Path.DirectorySeparatorChar ) 
178                     || fullPath.EndsWith( Path.AltDirectorySeparatorChar )) )
179                     demandPath = fullPath + Path.DirectorySeparatorCharAsString;
180                 else
181                     demandPath = fullPath;
182             }
183             return demandPath;
184         }
185
186         [ResourceExposure(ResourceScope.Machine)]
187         [ResourceConsumption(ResourceScope.Machine)]
188         internal static void InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj)
189         {
190             InternalCreateDirectory(fullPath, path, dirSecurityObj, false);
191         }
192
193
194         [System.Security.SecuritySafeCritical]
195         [ResourceExposure(ResourceScope.Machine)]
196         [ResourceConsumption(ResourceScope.Machine)]
197         internal unsafe static void InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, bool checkHost)
198         {
199 #if FEATURE_MACL
200             DirectorySecurity dirSecurity = (DirectorySecurity)dirSecurityObj;
201 #endif // FEATURE_MACL
202
203             int length = fullPath.Length;
204
205             // We need to trim the trailing slash or the code will try to create 2 directories of the same name.
206             if (length >= 2 && Path.IsDirectorySeparator(fullPath[length - 1]))
207                 length--;
208             
209             int lengthRoot = Path.GetRootLength(fullPath);
210
211             // For UNC paths that are only // or /// 
212             if (length == 2 && Path.IsDirectorySeparator(fullPath[1]))
213                 throw new IOException(Environment.GetResourceString("IO.IO_CannotCreateDirectory", path));
214
215             // We can save a bunch of work if the directory we want to create already exists.  This also
216             // saves us in the case where sub paths are inaccessible (due to ERROR_ACCESS_DENIED) but the
217             // final path is accessable and the directory already exists.  For example, consider trying
218             // to create c:\Foo\Bar\Baz, where everything already exists but ACLS prevent access to c:\Foo
219             // and c:\Foo\Bar.  In that case, this code will think it needs to create c:\Foo, and c:\Foo\Bar
220             // and fail to due so, causing an exception to be thrown.  This is not what we want.
221             if (InternalExists(fullPath)) {
222                 return;
223             }
224
225             List<string> stackDir = new List<string>();
226
227             // Attempt to figure out which directories don't exist, and only
228             // create the ones we need.  Note that InternalExists may fail due
229             // to Win32 ACL's preventing us from seeing a directory, and this
230             // isn't threadsafe.
231
232             bool somepathexists = false;
233
234             if (length > lengthRoot) { // Special case root (fullpath = X:\\)
235                 int i = length-1;
236                 while (i >= lengthRoot && !somepathexists) {
237                     String dir = fullPath.Substring(0, i+1);
238                         
239                     if (!InternalExists(dir)) // Create only the ones missing
240                         stackDir.Add(dir);
241                     else
242                         somepathexists = true;
243                     
244                     while (i > lengthRoot && fullPath[i] != Path.DirectorySeparatorChar && fullPath[i] != Path.AltDirectorySeparatorChar) i--;
245                     i--;
246                 }
247             }
248
249             int count = stackDir.Count;
250
251             if (stackDir.Count != 0)
252             {
253                 String [] securityList = new String[stackDir.Count];
254                 stackDir.CopyTo(securityList, 0);
255                 for (int j = 0 ; j < securityList.Length; j++)
256                     securityList[j] += "\\."; // leaf will never have a slash at the end
257
258                 // Security check for all directories not present only.
259 #if !FEATURE_PAL  && FEATURE_MACL
260                 AccessControlActions control = (dirSecurity == null) ? AccessControlActions.None : AccessControlActions.Change;
261                 new FileIOPermission(FileIOPermissionAccess.Write, control, securityList, false, false ).Demand();
262 #else
263 #if FEATURE_CORECLR
264                 if (checkHost)
265                 {
266                     foreach (String demandPath in securityList) 
267                     {
268                         FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, String.Empty, demandPath);
269                         state.EnsureState();
270                     }
271                 }
272 #else
273                 new FileIOPermission(FileIOPermissionAccess.Write, securityList, false, false ).Demand();
274 #endif
275 #endif //!FEATURE_PAL  && FEATURE_MACL
276             }
277
278             // If we were passed a DirectorySecurity, convert it to a security
279             // descriptor and set it in he call to CreateDirectory.
280             Win32Native.SECURITY_ATTRIBUTES secAttrs = null;
281 #if FEATURE_MACL
282             if (dirSecurity != null) {
283                 secAttrs = new Win32Native.SECURITY_ATTRIBUTES();
284                 secAttrs.nLength = (int)Marshal.SizeOf(secAttrs);
285
286                 // For ACL's, get the security descriptor from the FileSecurity.
287                 byte[] sd = dirSecurity.GetSecurityDescriptorBinaryForm();
288                 byte * bytesOnStack = stackalloc byte[sd.Length];
289                 Buffer.Memcpy(bytesOnStack, 0, sd, 0, sd.Length);
290                 secAttrs.pSecurityDescriptor = bytesOnStack;
291             }
292 #endif
293
294             bool r = true;
295             int firstError = 0;
296             String errorString = path;
297             // If all the security checks succeeded create all the directories
298             while (stackDir.Count > 0) {
299                 String name = stackDir[stackDir.Count - 1];
300                 stackDir.RemoveAt(stackDir.Count - 1);
301                 if (name.Length >= Path.MAX_DIRECTORY_PATH)
302                     throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
303                 r = Win32Native.CreateDirectory(name, secAttrs);
304                 if (!r && (firstError == 0)) {
305                     int currentError = Marshal.GetLastWin32Error();
306                     // While we tried to avoid creating directories that don't
307                     // exist above, there are at least two cases that will 
308                     // cause us to see ERROR_ALREADY_EXISTS here.  InternalExists 
309                     // can fail because we didn't have permission to the 
310                     // directory.  Secondly, another thread or process could
311                     // create the directory between the time we check and the
312                     // time we try using the directory.  Thirdly, it could
313                     // fail because the target does exist, but is a file.
314                     if (currentError != Win32Native.ERROR_ALREADY_EXISTS)
315                         firstError = currentError;
316                     else {
317                         // If there's a file in this directory's place, or if we have ERROR_ACCESS_DENIED when checking if the directory already exists throw.
318                         if (File.InternalExists(name) || (!InternalExists(name, out currentError) && currentError == Win32Native.ERROR_ACCESS_DENIED)) {
319                             firstError = currentError;
320                             // Give the user a nice error message, but don't leak path information.
321                             try {
322 #if FEATURE_CORECLR
323                                 if (checkHost)
324                                 {
325                                     FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, GetDemandDir(name, true));
326                                     state.EnsureState();
327                                 }
328 #else
329                                 new FileIOPermission(FileIOPermissionAccess.PathDiscovery, GetDemandDir(name, true)).Demand();
330 #endif // FEATURE_CORECLR
331                                 errorString = name;
332                             }
333                             catch(SecurityException) {}
334                         }
335                     }
336                 }
337             }
338
339             // We need this check to mask OS differences
340             // Handle CreateDirectory("X:\\foo") when X: doesn't exist. Similarly for n/w paths.
341             if ((count == 0) && !somepathexists) {
342                 String root = InternalGetDirectoryRoot(fullPath);
343                 if (!InternalExists(root)) {
344                     // Extract the root from the passed in path again for security.
345                     __Error.WinIOError(Win32Native.ERROR_PATH_NOT_FOUND, InternalGetDirectoryRoot(path));
346                 }
347                 return;
348             }
349
350             // Only throw an exception if creating the exact directory we 
351             // wanted failed to work correctly.
352             if (!r && (firstError != 0)) {
353                 __Error.WinIOError(firstError, errorString);
354             }
355         }
356       
357        
358         // Tests if the given path refers to an existing DirectoryInfo on disk.
359         // 
360         // Your application must have Read permission to the directory's
361         // contents.
362         //
363         [System.Security.SecuritySafeCritical]  // auto-generated
364         [ResourceExposure(ResourceScope.Machine)]
365         [ResourceConsumption(ResourceScope.Machine)]
366         public static bool Exists(String path)
367         {
368             return InternalExistsHelper(path, true);
369         }
370
371         [System.Security.SecurityCritical]
372         [ResourceExposure(ResourceScope.Machine)]
373         [ResourceConsumption(ResourceScope.Machine)]
374         internal static bool UnsafeExists(String path)
375         {
376             return InternalExistsHelper(path, false);
377         }
378
379         [System.Security.SecurityCritical]
380         [ResourceExposure(ResourceScope.Machine)]
381         [ResourceConsumption(ResourceScope.Machine)]
382         internal static bool InternalExistsHelper(String path, bool checkHost) {
383             try
384             {
385                 if (path == null)
386                     return false;
387                 if (path.Length == 0)
388                     return false;
389
390                 // Get fully qualified file name ending in \* for security check
391
392                 String fullPath = Path.GetFullPathInternal(path);
393                 String demandPath = GetDemandDir(fullPath, true);
394
395 #if FEATURE_CORECLR
396                 if (checkHost)
397                 {
398                     FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, path, demandPath);
399                     state.EnsureState();
400                 }
401 #else
402                 FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandPath, false, false);
403 #endif
404
405
406                 return InternalExists(fullPath);
407             }
408             catch (ArgumentException) { }
409             catch (NotSupportedException) { }  // Security can throw this on ":"
410             catch (SecurityException) { }
411             catch (IOException) { }
412             catch (UnauthorizedAccessException)
413             {
414 #if !FEATURE_PAL
415                 Contract.Assert(false, "Ignore this assert and send a repro to [....]. This assert was tracking purposes only.");
416 #endif //!FEATURE_PAL
417             }
418             return false;
419         }
420
421         // Determine whether path describes an existing directory
422         // on disk, avoiding security checks.
423         [System.Security.SecurityCritical]  // auto-generated
424         [ResourceExposure(ResourceScope.Machine)]
425         [ResourceConsumption(ResourceScope.Machine)]
426         internal static bool InternalExists(String path) {
427             int lastError = Win32Native.ERROR_SUCCESS;
428             return InternalExists(path, out lastError);
429         }
430
431         // Determine whether path describes an existing directory
432         // on disk, avoiding security checks.
433         [System.Security.SecurityCritical]  // auto-generated
434         [ResourceExposure(ResourceScope.Machine)]
435         [ResourceConsumption(ResourceScope.Machine)]
436         internal static bool InternalExists(String path, out int lastError) {
437             Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
438             lastError = File.FillAttributeInfo(path, ref data, false, true);
439
440             return (lastError == 0) && (data.fileAttributes != -1)
441                     && ((data.fileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) != 0);
442         }
443
444         [ResourceExposure(ResourceScope.Machine)]
445         [ResourceConsumption(ResourceScope.Machine)]
446         public static void SetCreationTime(String path,DateTime creationTime)
447         {
448             SetCreationTimeUtc(path, creationTime.ToUniversalTime());
449         }
450
451         [System.Security.SecuritySafeCritical]  // auto-generated
452         [ResourceExposure(ResourceScope.Machine)]
453         [ResourceConsumption(ResourceScope.Machine)]
454         public unsafe static void SetCreationTimeUtc(String path,DateTime creationTimeUtc)
455         {
456             using (SafeFileHandle handle = Directory.OpenHandle(path)) {
457                 Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(creationTimeUtc.ToFileTimeUtc());
458                 bool r = Win32Native.SetFileTime(handle, &fileTime, null, null);
459                 if (!r)
460                 {
461                     int errorCode = Marshal.GetLastWin32Error();
462                     __Error.WinIOError(errorCode, path);
463                 }
464             }
465         }
466
467         [ResourceExposure(ResourceScope.Machine)]
468         [ResourceConsumption(ResourceScope.Machine)]
469         public static DateTime GetCreationTime(String path)
470         {
471             return File.GetCreationTime(path);
472         }
473
474         [ResourceExposure(ResourceScope.Machine)]
475         [ResourceConsumption(ResourceScope.Machine)]
476         public static DateTime GetCreationTimeUtc(String path)
477         {
478             return File.GetCreationTimeUtc(path);
479         }
480
481         [ResourceExposure(ResourceScope.Machine)]
482         [ResourceConsumption(ResourceScope.Machine)]
483         public static void SetLastWriteTime(String path,DateTime lastWriteTime)
484         {
485             SetLastWriteTimeUtc(path, lastWriteTime.ToUniversalTime());
486         }
487
488         [System.Security.SecuritySafeCritical]  // auto-generated
489         [ResourceExposure(ResourceScope.Machine)]
490         [ResourceConsumption(ResourceScope.Machine)]
491         public unsafe static void SetLastWriteTimeUtc(String path,DateTime lastWriteTimeUtc)
492         {
493             using (SafeFileHandle handle = Directory.OpenHandle(path)) {
494                 Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(lastWriteTimeUtc.ToFileTimeUtc());
495                 bool r = Win32Native.SetFileTime(handle,  null, null, &fileTime);
496                 if (!r)
497                 {
498                     int errorCode = Marshal.GetLastWin32Error();
499                     __Error.WinIOError(errorCode, path);
500                 }
501             }
502         }
503
504         [ResourceExposure(ResourceScope.Machine)]
505         [ResourceConsumption(ResourceScope.Machine)]
506         public static DateTime GetLastWriteTime(String path)
507         {
508             return File.GetLastWriteTime(path);
509         }
510
511         [ResourceExposure(ResourceScope.Machine)]
512         [ResourceConsumption(ResourceScope.Machine)]
513         public static DateTime GetLastWriteTimeUtc(String path)
514         {
515             return File.GetLastWriteTimeUtc(path);
516         }
517
518         [ResourceExposure(ResourceScope.Machine)]
519         [ResourceConsumption(ResourceScope.Machine)]
520         public static void SetLastAccessTime(String path,DateTime lastAccessTime)
521         {
522             SetLastAccessTimeUtc(path, lastAccessTime.ToUniversalTime());
523         }
524
525         [System.Security.SecuritySafeCritical]  // auto-generated
526         [ResourceExposure(ResourceScope.Machine)]
527         [ResourceConsumption(ResourceScope.Machine)]
528         public unsafe static void SetLastAccessTimeUtc(String path,DateTime lastAccessTimeUtc)
529         {
530             using (SafeFileHandle handle = Directory.OpenHandle(path)) {
531                 Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(lastAccessTimeUtc.ToFileTimeUtc());
532                 bool r = Win32Native.SetFileTime(handle,  null, &fileTime, null);
533                 if (!r)
534                 {
535                     int errorCode = Marshal.GetLastWin32Error();
536                     __Error.WinIOError(errorCode, path);
537                 }
538             }
539         }
540
541         [ResourceExposure(ResourceScope.Machine)]
542         [ResourceConsumption(ResourceScope.Machine)]
543         public static DateTime GetLastAccessTime(String path)
544         {
545             return File.GetLastAccessTime(path);
546         }
547
548         [ResourceExposure(ResourceScope.Machine)]
549         [ResourceConsumption(ResourceScope.Machine)]
550         public static DateTime GetLastAccessTimeUtc(String path)
551         {
552             return File.GetLastAccessTimeUtc(path);
553         }       
554  
555 #if FEATURE_MACL
556         [ResourceExposure(ResourceScope.Machine)]
557         [ResourceConsumption(ResourceScope.Machine)]
558         public static DirectorySecurity GetAccessControl(String path)
559         {
560             return new DirectorySecurity(path, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group);
561         }
562
563         [ResourceExposure(ResourceScope.Machine)]
564         [ResourceConsumption(ResourceScope.Machine)]
565         public static DirectorySecurity GetAccessControl(String path, AccessControlSections includeSections)
566         {
567             return new DirectorySecurity(path, includeSections);
568         }
569
570         [System.Security.SecuritySafeCritical]  // auto-generated
571         [ResourceExposure(ResourceScope.Machine)]
572         [ResourceConsumption(ResourceScope.Machine)]
573         public static void SetAccessControl(String path, DirectorySecurity directorySecurity)
574         {
575             if (directorySecurity == null)
576                 throw new ArgumentNullException("directorySecurity");
577             Contract.EndContractBlock();
578
579             String fullPath = Path.GetFullPathInternal(path);
580             directorySecurity.Persist(fullPath);
581         }
582 #endif
583
584         // Returns an array of filenames in the DirectoryInfo specified by path
585         [ResourceExposure(ResourceScope.Machine)]
586         [ResourceConsumption(ResourceScope.Machine)]
587         public static String[] GetFiles(String path)
588         {
589             if (path == null)
590                 throw new ArgumentNullException("path");
591             Contract.Ensures(Contract.Result<String[]>() != null);
592             Contract.EndContractBlock();
593
594             return InternalGetFiles(path, "*", SearchOption.TopDirectoryOnly);
595         }
596
597         // Returns an array of Files in the current DirectoryInfo matching the 
598         // given search pattern (ie, "*.txt").
599         [ResourceExposure(ResourceScope.Machine)]
600         [ResourceConsumption(ResourceScope.Machine)]
601         public static String[] GetFiles(String path, String searchPattern)
602         {
603             if (path == null)
604                 throw new ArgumentNullException("path");
605             if (searchPattern == null)
606                 throw new ArgumentNullException("searchPattern");
607             Contract.Ensures(Contract.Result<String[]>() != null);
608             Contract.EndContractBlock();
609
610             return InternalGetFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
611         }
612
613         // Returns an array of Files in the current DirectoryInfo matching the 
614         // given search pattern (ie, "*.txt") and search option
615         [ResourceExposure(ResourceScope.Machine)]
616         [ResourceConsumption(ResourceScope.Machine)]
617         public static String[] GetFiles(String path, String searchPattern, SearchOption searchOption)
618         {
619             if (path == null)
620                 throw new ArgumentNullException("path");
621             if (searchPattern == null)
622                 throw new ArgumentNullException("searchPattern");
623             if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
624                 throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
625             Contract.Ensures(Contract.Result<String[]>() != null);
626             Contract.EndContractBlock();
627             
628             return InternalGetFiles(path, searchPattern, searchOption);
629         }
630
631         // Returns an array of Files in the current DirectoryInfo matching the 
632         // given search pattern (ie, "*.txt") and search option
633         [ResourceExposure(ResourceScope.Machine)]
634         [ResourceConsumption(ResourceScope.Machine)]
635         private static String[] InternalGetFiles(String path, String searchPattern, SearchOption searchOption)
636         {
637             Contract.Requires(path != null);
638             Contract.Requires(searchPattern != null);
639             Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
640
641             return InternalGetFileDirectoryNames(path, path, searchPattern, true, false, searchOption, true);
642         }
643
644         [System.Security.SecurityCritical]
645         [ResourceExposure(ResourceScope.Machine)]
646         [ResourceConsumption(ResourceScope.Machine)]
647         internal static String[] UnsafeGetFiles(String path, String searchPattern, SearchOption searchOption)
648         {
649             Contract.Requires(path != null);
650             Contract.Requires(searchPattern != null);
651             Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
652
653             return InternalGetFileDirectoryNames(path, path, searchPattern, true, false, searchOption, false);
654         }
655
656         // Returns an array of Directories in the current directory.
657         [ResourceExposure(ResourceScope.Machine)]
658         [ResourceConsumption(ResourceScope.Machine)]
659         public static String[] GetDirectories(String path)
660         {
661             if (path == null)
662                 throw new ArgumentNullException("path");
663             Contract.Ensures(Contract.Result<String[]>() != null);
664             Contract.EndContractBlock();
665
666             return InternalGetDirectories(path, "*", SearchOption.TopDirectoryOnly);
667         }
668
669         // Returns an array of Directories in the current DirectoryInfo matching the 
670         // given search criteria (ie, "*.txt").
671         [ResourceExposure(ResourceScope.Machine)]
672         [ResourceConsumption(ResourceScope.Machine)]
673         public static String[] GetDirectories(String path, String searchPattern)
674         {
675             if (path == null)
676                 throw new ArgumentNullException("path");
677             if (searchPattern == null)
678                 throw new ArgumentNullException("searchPattern");
679             Contract.Ensures(Contract.Result<String[]>() != null);
680             Contract.EndContractBlock();
681
682             return InternalGetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
683         }
684
685         // Returns an array of Directories in the current DirectoryInfo matching the 
686         // given search criteria (ie, "*.txt").
687         [ResourceExposure(ResourceScope.Machine)]
688         [ResourceConsumption(ResourceScope.Machine)]
689         public static String[] GetDirectories(String path, String searchPattern, SearchOption searchOption)
690         {
691             if (path == null)
692                 throw new ArgumentNullException("path");
693             if (searchPattern == null)
694                 throw new ArgumentNullException("searchPattern");
695             if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
696                 throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
697             Contract.Ensures(Contract.Result<String[]>() != null);
698             Contract.EndContractBlock();
699
700             return InternalGetDirectories(path, searchPattern, searchOption);
701         }
702
703         // Returns an array of Directories in the current DirectoryInfo matching the 
704         // given search criteria (ie, "*.txt").
705         [ResourceExposure(ResourceScope.Machine)]
706         [ResourceConsumption(ResourceScope.Machine)]
707         private static String[] InternalGetDirectories(String path, String searchPattern, SearchOption searchOption)
708         {
709             Contract.Requires(path != null);
710             Contract.Requires(searchPattern != null);
711             Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
712             Contract.Ensures(Contract.Result<String[]>() != null);
713
714             return InternalGetFileDirectoryNames(path, path, searchPattern, false, true, searchOption, true);
715         }
716
717         [System.Security.SecurityCritical]
718         [ResourceExposure(ResourceScope.Machine)]
719         [ResourceConsumption(ResourceScope.Machine)]
720         internal static String[] UnsafeGetDirectories(String path, String searchPattern, SearchOption searchOption)
721         {
722             Contract.Requires(path != null);
723             Contract.Requires(searchPattern != null);
724             Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
725             Contract.Ensures(Contract.Result<String[]>() != null);
726
727             return InternalGetFileDirectoryNames(path, path, searchPattern, false, true, searchOption, false);
728         }
729             
730         // Returns an array of strongly typed FileSystemInfo entries in the path
731         [ResourceExposure(ResourceScope.Machine)]
732         [ResourceConsumption(ResourceScope.Machine)]
733         public static String[] GetFileSystemEntries(String path)
734         {
735             if (path == null)
736                 throw new ArgumentNullException("path");
737             Contract.Ensures(Contract.Result<String[]>() != null);
738             Contract.EndContractBlock();
739
740             return InternalGetFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly);
741         }
742
743         // Returns an array of strongly typed FileSystemInfo entries in the path with the
744         // given search criteria (ie, "*.txt"). We disallow .. as a part of the search criteria
745         [ResourceExposure(ResourceScope.Machine)]
746         [ResourceConsumption(ResourceScope.Machine)]
747         public static String[] GetFileSystemEntries(String path, String searchPattern)
748         {
749             if (path == null)
750                 throw new ArgumentNullException("path");
751             if (searchPattern == null)
752                 throw new ArgumentNullException("searchPattern");
753             Contract.Ensures(Contract.Result<String[]>() != null);
754             Contract.EndContractBlock();
755
756             return InternalGetFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly);
757         }
758
759         // Returns an array of strongly typed FileSystemInfo entries in the path with the
760         // given search criteria (ie, "*.txt"). We disallow .. as a part of the search criteria
761         [ResourceExposure(ResourceScope.Machine)]
762         [ResourceConsumption(ResourceScope.Machine)]
763         public static String[] GetFileSystemEntries(String path, String searchPattern, SearchOption searchOption)
764         {
765             if (path == null)
766                 throw new ArgumentNullException("path");
767             if (searchPattern == null)
768                 throw new ArgumentNullException("searchPattern");
769             if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
770                 throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
771             Contract.Ensures(Contract.Result<String[]>() != null);
772             Contract.EndContractBlock();
773
774             return InternalGetFileSystemEntries(path, searchPattern, searchOption);
775         }
776
777         [ResourceExposure(ResourceScope.Machine)]
778         [ResourceConsumption(ResourceScope.Machine)]
779         private static String[] InternalGetFileSystemEntries(String path, String searchPattern, SearchOption searchOption)
780         {
781             Contract.Requires(path != null);
782             Contract.Requires(searchPattern != null);
783             Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
784
785             return InternalGetFileDirectoryNames(path, path, searchPattern, true, true, searchOption, true);
786         }
787
788
789         // Private class that holds search data that is passed around 
790         // in the heap based stack recursion
791         internal sealed class SearchData
792         {
793             public SearchData(String fullPath, String userPath, SearchOption searchOption)
794             {
795                 Contract.Requires(fullPath != null && fullPath.Length > 0);
796                 Contract.Requires(userPath != null && userPath.Length > 0);
797                 Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
798
799                 this.fullPath = fullPath;
800                 this.userPath = userPath;
801                 this.searchOption = searchOption;
802             }
803
804             public readonly string fullPath;     // Fully qualified search path excluding the search criteria in the end (ex, c:\temp\bar\foo)
805             public readonly string userPath;     // User specified path (ex, bar\foo)
806             public readonly SearchOption searchOption;
807         }
808
809
810         // Returns fully qualified user path of dirs/files that matches the search parameters. 
811         // For recursive search this method will search through all the sub dirs  and execute 
812         // the given search criteria against every dir.
813         // For all the dirs/files returned, it will then demand path discovery permission for 
814         // their parent folders (it will avoid duplicate permission checks)
815         [ResourceExposure(ResourceScope.Machine)]
816         [ResourceConsumption(ResourceScope.Machine)]
817         internal static String[] InternalGetFileDirectoryNames(String path, String userPathOriginal, String searchPattern, bool includeFiles, bool includeDirs, SearchOption searchOption, bool checkHost)
818         {
819             Contract.Requires(path != null);
820             Contract.Requires(userPathOriginal != null);
821             Contract.Requires(searchPattern != null);
822             Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
823
824             IEnumerable<String> enble = FileSystemEnumerableFactory.CreateFileNameIterator(
825                                                                                 path, userPathOriginal, searchPattern,
826                                                                                 includeFiles, includeDirs, searchOption, checkHost);
827             List<String> fileList = new List<String>(enble);
828             return fileList.ToArray();
829         }
830
831         [ResourceExposure(ResourceScope.Machine)]
832         [ResourceConsumption(ResourceScope.Machine)]
833         public static IEnumerable<String> EnumerateDirectories(String path)
834         {
835             if (path == null)
836                 throw new ArgumentNullException("path");
837             Contract.EndContractBlock();
838
839             return InternalEnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly);
840         }
841
842         [ResourceExposure(ResourceScope.Machine)]
843         [ResourceConsumption(ResourceScope.Machine)]
844         public static IEnumerable<String> EnumerateDirectories(String path, String searchPattern)
845         {
846             if (path == null)
847                 throw new ArgumentNullException("path");
848             if (searchPattern == null)
849                 throw new ArgumentNullException("searchPattern");
850             Contract.EndContractBlock();
851
852             return InternalEnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
853         }
854
855         [ResourceExposure(ResourceScope.Machine)]
856         [ResourceConsumption(ResourceScope.Machine)]
857         public static IEnumerable<String> EnumerateDirectories(String path, String searchPattern, SearchOption searchOption)
858         {
859             if (path == null)
860                 throw new ArgumentNullException("path");
861             if (searchPattern == null)
862                 throw new ArgumentNullException("searchPattern");
863             if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
864                 throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
865             Contract.EndContractBlock();
866
867             return InternalEnumerateDirectories(path, searchPattern, searchOption);
868         }
869
870         [ResourceExposure(ResourceScope.Machine)]
871         [ResourceConsumption(ResourceScope.Machine)]
872         private static IEnumerable<String> InternalEnumerateDirectories(String path, String searchPattern, SearchOption searchOption)
873         {
874             Contract.Requires(path != null);
875             Contract.Requires(searchPattern != null);
876             Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
877
878             return EnumerateFileSystemNames(path, searchPattern, searchOption, false, true);
879         }
880
881         [ResourceExposure(ResourceScope.Machine)]
882         [ResourceConsumption(ResourceScope.Machine)]
883         public static IEnumerable<String> EnumerateFiles(String path)
884         {
885             if (path == null)
886                 throw new ArgumentNullException("path");
887             Contract.Ensures(Contract.Result<IEnumerable<String>>() != null);
888             Contract.EndContractBlock();
889
890             return InternalEnumerateFiles(path, "*", SearchOption.TopDirectoryOnly);
891         }
892
893         [ResourceExposure(ResourceScope.Machine)]
894         [ResourceConsumption(ResourceScope.Machine)]
895         public static IEnumerable<String> EnumerateFiles(String path, String searchPattern)
896         {
897             if (path == null)
898                 throw new ArgumentNullException("path");
899             if (searchPattern == null)
900                 throw new ArgumentNullException("searchPattern");
901             Contract.Ensures(Contract.Result<IEnumerable<String>>() != null);
902             Contract.EndContractBlock();
903
904             return InternalEnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
905         }
906
907         [ResourceExposure(ResourceScope.Machine)]
908         [ResourceConsumption(ResourceScope.Machine)]
909         public static IEnumerable<String> EnumerateFiles(String path, String searchPattern, SearchOption searchOption)
910         {
911             if (path == null)
912                 throw new ArgumentNullException("path");
913             if (searchPattern == null)
914                 throw new ArgumentNullException("searchPattern");
915             if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
916                 throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
917             Contract.Ensures(Contract.Result<IEnumerable<String>>() != null);
918             Contract.EndContractBlock();
919
920             return InternalEnumerateFiles(path, searchPattern, searchOption);
921         }
922
923         [ResourceExposure(ResourceScope.Machine)]
924         [ResourceConsumption(ResourceScope.Machine)]
925         private static IEnumerable<String> InternalEnumerateFiles(String path, String searchPattern, SearchOption searchOption)
926         {
927             Contract.Requires(path != null);
928             Contract.Requires(searchPattern != null);
929             Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
930             Contract.Ensures(Contract.Result<IEnumerable<String>>() != null);
931
932             return EnumerateFileSystemNames(path, searchPattern, searchOption, true, false);
933         }
934
935         [ResourceExposure(ResourceScope.Machine)]
936         [ResourceConsumption(ResourceScope.Machine)]
937         public static IEnumerable<String> EnumerateFileSystemEntries(String path)
938         {
939             if (path == null)
940                 throw new ArgumentNullException("path");
941             Contract.Ensures(Contract.Result<IEnumerable<String>>() != null);
942             Contract.EndContractBlock();
943
944             return InternalEnumerateFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly);
945         }
946
947         [ResourceExposure(ResourceScope.Machine)]
948         [ResourceConsumption(ResourceScope.Machine)]
949         public static IEnumerable<String> EnumerateFileSystemEntries(String path, String searchPattern)
950         {
951             if (path == null)
952                 throw new ArgumentNullException("path");
953             if (searchPattern == null)
954                 throw new ArgumentNullException("searchPattern");
955             Contract.Ensures(Contract.Result<IEnumerable<String>>() != null);
956             Contract.EndContractBlock();
957
958             return InternalEnumerateFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly);
959         }
960
961         [ResourceExposure(ResourceScope.Machine)]
962         [ResourceConsumption(ResourceScope.Machine)]
963         public static IEnumerable<String> EnumerateFileSystemEntries(String path, String searchPattern, SearchOption searchOption)
964         {
965             if (path == null)
966                 throw new ArgumentNullException("path");
967             if (searchPattern == null)
968                 throw new ArgumentNullException("searchPattern");
969             if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
970                 throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
971             Contract.Ensures(Contract.Result<IEnumerable<String>>() != null);
972             Contract.EndContractBlock();
973
974             return InternalEnumerateFileSystemEntries(path, searchPattern, searchOption);
975         }
976
977         [ResourceExposure(ResourceScope.Machine)]
978         [ResourceConsumption(ResourceScope.Machine)]
979         private static IEnumerable<String> InternalEnumerateFileSystemEntries(String path, String searchPattern, SearchOption searchOption)
980         {
981             Contract.Requires(path != null);
982             Contract.Requires(searchPattern != null);
983             Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
984             Contract.Ensures(Contract.Result<IEnumerable<String>>() != null);
985
986             return EnumerateFileSystemNames(path, searchPattern, searchOption, true, true);
987         }
988
989         private static IEnumerable<String> EnumerateFileSystemNames(String path, String searchPattern, SearchOption searchOption,
990                                                             bool includeFiles, bool includeDirs)
991         {
992             Contract.Requires(path != null);
993             Contract.Requires(searchPattern != null);
994             Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
995             Contract.Ensures(Contract.Result<IEnumerable<String>>() != null);
996
997             return FileSystemEnumerableFactory.CreateFileNameIterator(path, path, searchPattern,
998                                                                         includeFiles, includeDirs, searchOption, true);
999         }
1000
1001         // Retrieves the names of the logical drives on this machine in the 
1002         // form "C:\". 
1003         // 
1004         // Your application must have System Info permission.
1005         // 
1006         [System.Security.SecuritySafeCritical]  // auto-generated
1007         public static String[] GetLogicalDrives()
1008         {
1009             Contract.Ensures(Contract.Result<String[]>() != null);
1010
1011 #pragma warning disable 618
1012             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
1013 #pragma warning restore 618
1014                                  
1015             int drives = Win32Native.GetLogicalDrives();
1016             if (drives==0)
1017                 __Error.WinIOError();
1018             uint d = (uint)drives;
1019             int count = 0;
1020             while (d != 0) {
1021                 if (((int)d & 1) != 0) count++;
1022                 d >>= 1;
1023             }
1024             String[] result = new String[count];
1025             char[] root = new char[] {'A', ':', '\\'};
1026             d = (uint)drives;
1027             count = 0;
1028             while (d != 0) {
1029                 if (((int)d & 1) != 0) {
1030                     result[count++] = new String(root);
1031                 }
1032                 d >>= 1;
1033                 root[0]++;
1034             }
1035             return result;
1036         }
1037
1038         [System.Security.SecuritySafeCritical]
1039         [ResourceExposure(ResourceScope.Machine)]
1040         [ResourceConsumption(ResourceScope.Machine)]
1041         public static String GetDirectoryRoot(String path) {
1042             if (path==null)
1043                 throw new ArgumentNullException("path");
1044             Contract.EndContractBlock();
1045             
1046             String fullPath = Path.GetFullPathInternal(path);
1047             String root = fullPath.Substring(0, Path.GetRootLength(fullPath));
1048             String demandPath = GetDemandDir(root, true);
1049
1050 #if FEATURE_CORECLR
1051             FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, path, demandPath);
1052             state.EnsureState();
1053 #else
1054             FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPath, false, false);
1055 #endif
1056                      
1057             return root;
1058         }
1059
1060         internal static String InternalGetDirectoryRoot(String path) {
1061               if (path == null) return null;
1062             return path.Substring(0, Path.GetRootLength(path));
1063         }
1064
1065          /*===============================CurrentDirectory===============================
1066         **Action:  Provides a getter and setter for the current directory.  The original
1067         **         current DirectoryInfo is the one from which the process was started.  
1068         **Returns: The current DirectoryInfo (from the getter).  Void from the setter.
1069         **Arguments: The current DirectoryInfo to which to switch to the setter.
1070         **Exceptions: 
1071         ==============================================================================*/
1072         [System.Security.SecuritySafeCritical]
1073         [ResourceExposure(ResourceScope.Machine)]
1074         [ResourceConsumption(ResourceScope.Machine)]
1075         public static String GetCurrentDirectory()
1076         {
1077             return InternalGetCurrentDirectory(true);
1078         }
1079
1080         [System.Security.SecurityCritical]
1081         [ResourceExposure(ResourceScope.Machine)]
1082         [ResourceConsumption(ResourceScope.Machine)]
1083         internal static String UnsafeGetCurrentDirectory()
1084         {
1085             return InternalGetCurrentDirectory(false);
1086         }
1087
1088         [System.Security.SecurityCritical]
1089         [ResourceExposure(ResourceScope.Machine)]
1090         [ResourceConsumption(ResourceScope.Machine)]
1091         private static String InternalGetCurrentDirectory(bool checkHost)
1092         {
1093             StringBuilder sb = StringBuilderCache.Acquire(Path.MAX_PATH + 1);
1094             if (Win32Native.GetCurrentDirectory(sb.Capacity, sb) == 0)
1095                 __Error.WinIOError();
1096             String currentDirectory = sb.ToString();
1097             // Note that if we have somehow put our command prompt into short
1098             // file name mode (ie, by running edlin or a DOS grep, etc), then
1099             // this will return a short file name.
1100             if (currentDirectory.IndexOf('~') >= 0) {
1101                 int r = Win32Native.GetLongPathName(currentDirectory, sb, sb.Capacity);
1102                 if (r == 0 || r >= Path.MAX_PATH) {                    
1103                     int errorCode = Marshal.GetLastWin32Error();
1104                     if (r >= Path.MAX_PATH)
1105                         errorCode = Win32Native.ERROR_FILENAME_EXCED_RANGE;
1106                     if (errorCode != Win32Native.ERROR_FILE_NOT_FOUND &&
1107                         errorCode != Win32Native.ERROR_PATH_NOT_FOUND &&
1108                         errorCode != Win32Native.ERROR_INVALID_FUNCTION &&  // by design - enough said.
1109                         errorCode != Win32Native.ERROR_ACCESS_DENIED)
1110                         __Error.WinIOError(errorCode, String.Empty);
1111                 }
1112                 currentDirectory = sb.ToString();
1113             }
1114             StringBuilderCache.Release(sb);
1115             String demandPath = GetDemandDir(currentDirectory, true);
1116
1117             
1118 #if FEATURE_CORECLR
1119             if (checkHost) 
1120             {
1121                 FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPath);
1122                 state.EnsureState();
1123             }
1124 #else
1125             new FileIOPermission( FileIOPermissionAccess.PathDiscovery, new String[] { demandPath }, false, false ).Demand();
1126 #endif
1127             return currentDirectory;
1128         }
1129
1130
1131         #if FEATURE_CORECLR
1132         [System.Security.SecurityCritical] // auto-generated
1133         #else
1134         [System.Security.SecuritySafeCritical]
1135         #endif
1136         [ResourceExposure(ResourceScope.Machine)]
1137         [ResourceConsumption(ResourceScope.Machine)]
1138         public static void SetCurrentDirectory(String path)
1139         {        
1140             if (path==null)
1141                 throw new ArgumentNullException("value");
1142             if (path.Length==0)
1143                 throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"));
1144             Contract.EndContractBlock();
1145             if (path.Length >= Path.MAX_PATH)
1146                 throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
1147                 
1148             // This will have some large effects on the rest of the runtime
1149             // and other appdomains in this process.  Demand unmanaged code.
1150 #pragma warning disable 618
1151             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
1152 #pragma warning restore 618
1153
1154             String fulldestDirName = Path.GetFullPathInternal(path);
1155             
1156             if (!Win32Native.SetCurrentDirectory(fulldestDirName)) {
1157                 // If path doesn't exist, this sets last error to 2 (File 
1158                 // not Found).  LEGACY: This may potentially have worked correctly
1159                 // on Win9x, maybe.
1160                 int errorCode = Marshal.GetLastWin32Error();
1161                 if (errorCode == Win32Native.ERROR_FILE_NOT_FOUND)
1162                     errorCode = Win32Native.ERROR_PATH_NOT_FOUND;
1163                 __Error.WinIOError(errorCode, fulldestDirName);
1164             }
1165         }
1166
1167         [System.Security.SecuritySafeCritical]
1168         [ResourceExposure(ResourceScope.Machine)]
1169         [ResourceConsumption(ResourceScope.Machine)]
1170         public static void Move(String sourceDirName,String destDirName) {
1171             InternalMove(sourceDirName, destDirName, true);
1172         }
1173
1174         [System.Security.SecurityCritical]
1175         [ResourceExposure(ResourceScope.Machine)]
1176         [ResourceConsumption(ResourceScope.Machine)]
1177         internal static void UnsafeMove(String sourceDirName,String destDirName) {
1178             InternalMove(sourceDirName, destDirName, false);
1179         }
1180
1181         [System.Security.SecurityCritical]
1182         [ResourceExposure(ResourceScope.Machine)]
1183         [ResourceConsumption(ResourceScope.Machine)]
1184         private static void InternalMove(String sourceDirName,String destDirName,bool checkHost) {
1185             if (sourceDirName==null)
1186                 throw new ArgumentNullException("sourceDirName");
1187             if (sourceDirName.Length==0)
1188                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "sourceDirName");
1189             
1190             if (destDirName==null)
1191                 throw new ArgumentNullException("destDirName");
1192             if (destDirName.Length==0)
1193                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destDirName");
1194             Contract.EndContractBlock();
1195
1196             String fullsourceDirName = Path.GetFullPathInternal(sourceDirName);
1197             String sourcePath = GetDemandDir(fullsourceDirName, false);
1198             
1199             if (sourcePath.Length >= Path.MAX_DIRECTORY_PATH)
1200                 throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
1201
1202             String fulldestDirName = Path.GetFullPathInternal(destDirName);
1203             String destPath = GetDemandDir(fulldestDirName, false);
1204
1205             if (destPath.Length >= Path.MAX_DIRECTORY_PATH)
1206                 throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
1207             
1208 #if FEATURE_CORECLR
1209             if (checkHost) {
1210                 FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Write | FileSecurityStateAccess.Read, sourceDirName, sourcePath);
1211                 FileSecurityState destState = new FileSecurityState(FileSecurityStateAccess.Write, destDirName, destPath);
1212                 sourceState.EnsureState();
1213                 destState.EnsureState();
1214             }
1215 #else
1216             FileIOPermission.QuickDemand(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, sourcePath, false, false);
1217             FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, destPath, false, false);
1218 #endif
1219            
1220             if (String.Compare(sourcePath, destPath, StringComparison.OrdinalIgnoreCase) == 0)
1221                 throw new IOException(Environment.GetResourceString("IO.IO_SourceDestMustBeDifferent"));
1222
1223             String sourceRoot = Path.GetPathRoot(sourcePath);
1224             String destinationRoot = Path.GetPathRoot(destPath);
1225             if (String.Compare(sourceRoot, destinationRoot, StringComparison.OrdinalIgnoreCase) != 0)
1226                 throw new IOException(Environment.GetResourceString("IO.IO_SourceDestMustHaveSameRoot"));           
1227     
1228             if (!Win32Native.MoveFile(sourceDirName, destDirName))
1229             {
1230                 int hr = Marshal.GetLastWin32Error();
1231                 if (hr == Win32Native.ERROR_FILE_NOT_FOUND) // Source dir not found
1232                 {
1233                     hr = Win32Native.ERROR_PATH_NOT_FOUND;
1234                     __Error.WinIOError(hr, fullsourceDirName);
1235                 }
1236                 // This check was originally put in for Win9x (unfortunately without special casing it to be for Win9x only). We can't change the NT codepath now for backcomp reasons.
1237                 if (hr == Win32Native.ERROR_ACCESS_DENIED) // WinNT throws IOException. This check is for Win9x. We can't change it for backcomp.
1238                     throw new IOException(Environment.GetResourceString("UnauthorizedAccess_IODenied_Path", sourceDirName), Win32Native.MakeHRFromErrorCode(hr));
1239                 __Error.WinIOError(hr, String.Empty);
1240             }
1241         }
1242
1243         [System.Security.SecuritySafeCritical]
1244         [ResourceExposure(ResourceScope.Machine)]
1245         [ResourceConsumption(ResourceScope.Machine)]
1246         public static void Delete(String path)
1247         {
1248             String fullPath = Path.GetFullPathInternal(path);
1249             Delete(fullPath, path, false, true);
1250         }
1251
1252         [System.Security.SecuritySafeCritical]
1253         [ResourceExposure(ResourceScope.Machine)]
1254         [ResourceConsumption(ResourceScope.Machine)]
1255         public static void Delete(String path, bool recursive)
1256         {
1257             String fullPath = Path.GetFullPathInternal(path);
1258             Delete(fullPath, path, recursive, true);
1259         }
1260
1261         [System.Security.SecurityCritical] 
1262         [ResourceExposure(ResourceScope.Machine)]
1263         [ResourceConsumption(ResourceScope.Machine)]
1264         internal static void UnsafeDelete(String path, bool recursive)
1265         {
1266             String fullPath = Path.GetFullPathInternal(path);
1267             Delete(fullPath, path, recursive, false);
1268         }
1269
1270         // Called from DirectoryInfo as well.  FullPath is fully qualified,
1271         // while the user path is used for feedback in exceptions.
1272         [System.Security.SecurityCritical]  // auto-generated
1273         [ResourceExposure(ResourceScope.Machine)]
1274         [ResourceConsumption(ResourceScope.Machine)]
1275         internal static void Delete(String fullPath, String userPath, bool recursive, bool checkHost)
1276         {
1277             String demandPath;
1278             
1279             // If not recursive, do permission check only on this directory
1280             // else check for the whole directory structure rooted below 
1281             demandPath = GetDemandDir(fullPath, !recursive);
1282             
1283 #if FEATURE_CORECLR
1284             if (checkHost) 
1285             {
1286                 FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, userPath, demandPath);
1287                 state.EnsureState();
1288             }
1289 #else
1290             // Make sure we have write permission to this directory
1291             new FileIOPermission(FileIOPermissionAccess.Write, new String[] { demandPath }, false, false ).Demand();
1292 #endif
1293
1294             // Do not recursively delete through reparse points.  Perhaps in a 
1295             // future version we will add a new flag to control this behavior, 
1296             // but for now we're much safer if we err on the conservative side.
1297             // This applies to symbolic links and mount points.
1298             Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
1299             int dataInitialised = File.FillAttributeInfo(fullPath, ref data, false, true);
1300             if (dataInitialised != 0) {
1301                 // Ensure we throw a DirectoryNotFoundException.
1302                 if (dataInitialised == Win32Native.ERROR_FILE_NOT_FOUND)
1303                     dataInitialised = Win32Native.ERROR_PATH_NOT_FOUND;
1304                 __Error.WinIOError(dataInitialised, fullPath);
1305             }
1306
1307             if (((FileAttributes)data.fileAttributes & FileAttributes.ReparsePoint) != 0)
1308                 recursive = false;
1309
1310             DeleteHelper(fullPath, userPath, recursive, true);
1311         }
1312
1313         // Note that fullPath is fully qualified, while userPath may be 
1314         // relative.  Use userPath for all exception messages to avoid leaking
1315         // fully qualified path information.
1316         [System.Security.SecurityCritical]  // auto-generated
1317         [ResourceExposure(ResourceScope.Machine)]
1318         [ResourceConsumption(ResourceScope.Machine)]
1319         private static void DeleteHelper(String fullPath, String userPath, bool recursive, bool throwOnTopLevelDirectoryNotFound)
1320         {
1321             bool r;
1322             int hr;
1323             Exception ex = null;
1324
1325             // Do not recursively delete through reparse points.  Perhaps in a 
1326             // future version we will add a new flag to control this behavior, 
1327             // but for now we're much safer if we err on the conservative side.
1328             // This applies to symbolic links and mount points.
1329             // Note the logic to check whether fullPath is a reparse point is
1330             // in Delete(String, String, bool), and will set "recursive" to false.
1331             // Note that Win32's DeleteFile and RemoveDirectory will just delete
1332             // the reparse point itself.
1333
1334             if (recursive) {
1335                 Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA();
1336                 
1337                 // Open a Find handle
1338                 using (SafeFindHandle hnd = Win32Native.FindFirstFile(fullPath+Path.DirectorySeparatorCharAsString+"*", data)) {
1339                     if (hnd.IsInvalid) {
1340                         hr = Marshal.GetLastWin32Error();
1341                         __Error.WinIOError(hr, fullPath);
1342                     }
1343         
1344                     do {
1345                         bool isDir = (0!=(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY));
1346                         if (isDir) {
1347                             // Skip ".", "..".
1348                             if (data.cFileName.Equals(".") || data.cFileName.Equals(".."))
1349                                 continue;
1350
1351                             // Recurse for all directories, unless they are 
1352                             // reparse points.  Do not follow mount points nor
1353                             // symbolic links, but do delete the reparse point 
1354                             // itself.
1355                             bool shouldRecurse = (0 == (data.dwFileAttributes & (int) FileAttributes.ReparsePoint));
1356                             if (shouldRecurse) {
1357                                 String newFullPath = Path.InternalCombine(fullPath, data.cFileName);
1358                                 String newUserPath = Path.InternalCombine(userPath, data.cFileName);                        
1359                                 try {
1360                                     DeleteHelper(newFullPath, newUserPath, recursive, false);
1361                                 }
1362                                 catch(Exception e) {
1363                                     if (ex == null) {
1364                                         ex = e;
1365                                     }
1366                                 }
1367                             }
1368                             else {
1369                                 // Check to see if this is a mount point, and
1370                                 // unmount it.
1371                                 if (data.dwReserved0 == Win32Native.IO_REPARSE_TAG_MOUNT_POINT) {
1372                                     // Use full path plus a trailing '\'
1373                                     String mountPoint = Path.InternalCombine(fullPath, data.cFileName + Path.DirectorySeparatorChar);
1374                                     r = Win32Native.DeleteVolumeMountPoint(mountPoint);
1375                                     if (!r) {
1376                                         hr = Marshal.GetLastWin32Error();
1377                                         if (hr != Win32Native.ERROR_PATH_NOT_FOUND) {
1378                                             try {
1379                                                 __Error.WinIOError(hr, data.cFileName);
1380                                             }
1381                                             catch(Exception e) {
1382                                                 if (ex == null) {
1383                                                     ex = e;
1384                                                 }
1385                                             }
1386                                         }
1387                                     }
1388                                 }
1389
1390                                 // RemoveDirectory on a symbolic link will
1391                                 // remove the link itself.
1392                                 String reparsePoint = Path.InternalCombine(fullPath, data.cFileName);
1393                                 r = Win32Native.RemoveDirectory(reparsePoint);
1394                                 if (!r) {
1395                                     hr = Marshal.GetLastWin32Error();
1396                                     if (hr != Win32Native.ERROR_PATH_NOT_FOUND) {
1397                                         try {
1398                                             __Error.WinIOError(hr, data.cFileName);
1399                                         }
1400                                         catch(Exception e) {
1401                                             if (ex == null) {
1402                                                 ex = e;
1403                                             }
1404                                         }
1405                                     }
1406                                 }
1407                             }
1408                         }
1409                         else {
1410                             String fileName = Path.InternalCombine(fullPath, data.cFileName);
1411                             r = Win32Native.DeleteFile(fileName);
1412                             if (!r) {
1413                                 hr = Marshal.GetLastWin32Error();
1414                                 if (hr != Win32Native.ERROR_FILE_NOT_FOUND) {
1415                                     try {
1416                                         __Error.WinIOError(hr, data.cFileName);
1417                                     }
1418                                     catch (Exception e) {
1419                                         if (ex == null) {
1420                                             ex = e;
1421                                         }
1422                                     }
1423                                 }
1424                             }
1425                         }
1426                     } while (Win32Native.FindNextFile(hnd, data));
1427                     // Make sure we quit with a sensible error.
1428                     hr = Marshal.GetLastWin32Error();
1429                 }
1430
1431                 if (ex != null) 
1432                     throw ex;
1433                 if (hr!=0 && hr!=Win32Native.ERROR_NO_MORE_FILES) 
1434                     __Error.WinIOError(hr, userPath);
1435             }
1436
1437             r = Win32Native.RemoveDirectory(fullPath);
1438             
1439             if (!r) {
1440                 hr = Marshal.GetLastWin32Error();
1441                 if (hr == Win32Native.ERROR_FILE_NOT_FOUND) // A dubious error code.
1442                     hr = Win32Native.ERROR_PATH_NOT_FOUND;
1443                 // This check was originally put in for Win9x (unfortunately without special casing it to be for Win9x only). We can't change the NT codepath now for backcomp reasons.
1444                 if (hr == Win32Native.ERROR_ACCESS_DENIED) 
1445                     throw new IOException(Environment.GetResourceString("UnauthorizedAccess_IODenied_Path", userPath));
1446
1447                 // don't throw the DirectoryNotFoundException since this is a subdir and there could be a ----
1448                 // between two Directory.Delete callers
1449                 if (hr == Win32Native.ERROR_PATH_NOT_FOUND && !throwOnTopLevelDirectoryNotFound)
1450                     return;  
1451
1452                 __Error.WinIOError(hr, fullPath);
1453             }
1454         }
1455
1456         // WinNT only. Win9x this code will not work.
1457         [System.Security.SecurityCritical]  // auto-generated
1458         [ResourceExposure(ResourceScope.Machine)]
1459         [ResourceConsumption(ResourceScope.Machine)]
1460         private static SafeFileHandle OpenHandle(String path)
1461         {
1462             String fullPath = Path.GetFullPathInternal(path);
1463             String root = Path.GetPathRoot(fullPath);
1464             if (root == fullPath && root[1] == Path.VolumeSeparatorChar)
1465                 throw new ArgumentException(Environment.GetResourceString("Arg_PathIsVolume"));
1466
1467 #if !FEATURE_CORECLR
1468             FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, GetDemandDir(fullPath, true), false, false);
1469 #endif
1470
1471             SafeFileHandle handle = Win32Native.SafeCreateFile (
1472                 fullPath,
1473                 GENERIC_WRITE,
1474                 (FileShare) (FILE_SHARE_WRITE|FILE_SHARE_DELETE),
1475                 null,
1476                 FileMode.Open,
1477                 FILE_FLAG_BACKUP_SEMANTICS,
1478                 IntPtr.Zero
1479             );
1480
1481             if (handle.IsInvalid) {
1482                 int hr = Marshal.GetLastWin32Error();
1483                 __Error.WinIOError(hr, fullPath);
1484             }
1485             return handle;
1486         }
1487
1488         private const int FILE_ATTRIBUTE_DIRECTORY = 0x00000010;    
1489         private const int GENERIC_WRITE = unchecked((int)0x40000000);
1490         private const int FILE_SHARE_WRITE = 0x00000002;
1491         private const int FILE_SHARE_DELETE = 0x00000004;
1492         private const int OPEN_EXISTING = 0x00000003;
1493         private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
1494     }
1495
1496 }
1497