// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== /*============================================================ ** ** Class: Directory ** ** Microsoft ** ** ** Purpose: Exposes routines for enumerating through a ** directory. ** ** April 11,2000 ** ===========================================================*/ using System; using System.Collections; using System.Collections.Generic; using System.Security; using System.Security.Permissions; using Microsoft.Win32; using Microsoft.Win32.SafeHandles; using System.Text; using System.Runtime.InteropServices; using System.Globalization; using System.Runtime.Versioning; using System.Diagnostics.Contracts; using System.Threading; #if FEATURE_MACL using System.Security.AccessControl; #endif namespace System.IO { [ComVisible(true)] public static partial class Directory { #if !MONO [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static DirectoryInfo GetParent(String path) { if (path==null) throw new ArgumentNullException("path"); if (path.Length==0) throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"), "path"); Contract.EndContractBlock(); String fullPath = Path.GetFullPathInternal(path); String s = Path.GetDirectoryName(fullPath); if (s==null) return null; return new DirectoryInfo(s); } [System.Security.SecuritySafeCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static DirectoryInfo CreateDirectory(String path) { if (path == null) throw new ArgumentNullException("path"); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty")); Contract.EndContractBlock(); #if FEATURE_LEGACYNETCF if(CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { System.Reflection.Assembly callingAssembly = System.Reflection.Assembly.GetCallingAssembly(); if(callingAssembly != null && !callingAssembly.IsProfileAssembly) { string caller = new System.Diagnostics.StackFrame(1).GetMethod().FullName; string callee = System.Reflection.MethodBase.GetCurrentMethod().FullName; throw new MethodAccessException(String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString("Arg_MethodAccessException_WithCaller"), caller, callee)); } } #endif // FEATURE_LEGACYNETCF return InternalCreateDirectoryHelper(path, true); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static DirectoryInfo UnsafeCreateDirectory(String path) { if (path == null) throw new ArgumentNullException("path"); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty")); Contract.EndContractBlock(); return InternalCreateDirectoryHelper(path, false); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static DirectoryInfo InternalCreateDirectoryHelper(string path, bool checkHost) { Contract.Requires(path != null); Contract.Requires(path.Length != 0); string fullPath = GetFullPathAndCheckPermissions(path, checkHost: checkHost); InternalCreateDirectory(fullPath, path, dirSecurityObj: null, checkHost: checkHost); // Call the internal DirectoryInfo constructor return new DirectoryInfo(fullPath, junk: false); } internal static string GetFullPathAndCheckPermissions(string path, bool checkHost, FileSecurityStateAccess access = FileSecurityStateAccess.Read) { String fullPath = Path.GetFullPathInternal(path); CheckPermissions(path, fullPath, checkHost: checkHost, access: access); return fullPath; } [SecuritySafeCritical] internal static void CheckPermissions(string displayPath, string fullPath, bool checkHost, FileSecurityStateAccess access = FileSecurityStateAccess.Read) { // You need read access to the directory to be returned back and write access to all the directories // that you need to create. If we fail any security checks we will not create any directories at all. // We attempt to create directories only after all the security checks have passed. This is avoid doing // a demand at every level. #if FEATURE_CORECLR if (checkHost) { FileSecurityState state = new FileSecurityState( access, path: displayPath, canonicalizedPath: GetDemandDir(fullPath, thisDirOnly: true)); state.EnsureState(); // do the check on the AppDomainManager to make sure this is allowed } #else // In full trust we want to avoid allocating another string via GetDemandDir() if (CodeAccessSecurityEngine.QuickCheckForAllDemands()) FileIOPermission.EmulateFileIOPermissionChecks(fullPath); else FileIOPermission.QuickDemand( (FileIOPermissionAccess)access, GetDemandDir(fullPath, thisDirOnly: true), checkForDuplicates: false, needFullPath: false); #endif } #if FEATURE_MACL [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static DirectoryInfo CreateDirectory(String path, DirectorySecurity directorySecurity) { if (path==null) throw new ArgumentNullException("path"); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty")); Contract.EndContractBlock(); string fullPath = GetFullPathAndCheckPermissions(path, checkHost: true); InternalCreateDirectory(fullPath, path, directorySecurity); return new DirectoryInfo(fullPath, false); } #endif // FEATURE_MACL // Input to this method should already be fullpath. This method will ensure that we append // the trailing slash only when appropriate and when thisDirOnly is specified append a "." // at the end of the path to indicate that the demand is only for the fullpath and not // everything underneath it. [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.None, ResourceScope.None)] internal static String GetDemandDir(string fullPath, bool thisDirOnly) { String demandPath; if (thisDirOnly) { if (fullPath.EndsWith( Path.DirectorySeparatorChar ) || fullPath.EndsWith( Path.AltDirectorySeparatorChar ) ) demandPath = fullPath + "."; else demandPath = fullPath + Path.DirectorySeparatorCharAsString + "."; } else { if (!(fullPath.EndsWith( Path.DirectorySeparatorChar ) || fullPath.EndsWith( Path.AltDirectorySeparatorChar )) ) demandPath = fullPath + Path.DirectorySeparatorCharAsString; else demandPath = fullPath; } return demandPath; } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static void InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj) { InternalCreateDirectory(fullPath, path, dirSecurityObj, false); } [System.Security.SecuritySafeCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal unsafe static void InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, bool checkHost) { #if FEATURE_MACL DirectorySecurity dirSecurity = (DirectorySecurity)dirSecurityObj; #endif // FEATURE_MACL int length = fullPath.Length; // We need to trim the trailing slash or the code will try to create 2 directories of the same name. if (length >= 2 && Path.IsDirectorySeparator(fullPath[length - 1])) length--; int lengthRoot = Path.GetRootLength(fullPath); // For UNC paths that are only // or /// if (length == 2 && Path.IsDirectorySeparator(fullPath[1])) throw new IOException(Environment.GetResourceString("IO.IO_CannotCreateDirectory", path)); // We can save a bunch of work if the directory we want to create already exists. This also // saves us in the case where sub paths are inaccessible (due to ERROR_ACCESS_DENIED) but the // final path is accessable and the directory already exists. For example, consider trying // to create c:\Foo\Bar\Baz, where everything already exists but ACLS prevent access to c:\Foo // and c:\Foo\Bar. In that case, this code will think it needs to create c:\Foo, and c:\Foo\Bar // and fail to due so, causing an exception to be thrown. This is not what we want. if (InternalExists(fullPath)) { return; } List stackDir = new List(); // Attempt to figure out which directories don't exist, and only // create the ones we need. Note that InternalExists may fail due // to Win32 ACL's preventing us from seeing a directory, and this // isn't threadsafe. bool somepathexists = false; if (length > lengthRoot) { // Special case root (fullpath = X:\\) int i = length-1; while (i >= lengthRoot && !somepathexists) { String dir = fullPath.Substring(0, i+1); if (!InternalExists(dir)) // Create only the ones missing stackDir.Add(dir); else somepathexists = true; while (i > lengthRoot && fullPath[i] != Path.DirectorySeparatorChar && fullPath[i] != Path.AltDirectorySeparatorChar) i--; i--; } } int count = stackDir.Count; if (stackDir.Count != 0 #if FEATURE_CAS_POLICY // All demands in full trust domains are no-ops, so skip // // The full path went through validity checks by being passed through FileIOPermissions already. // As a sub string of the full path can't fail the checks if the full path passes. && !CodeAccessSecurityEngine.QuickCheckForAllDemands() #endif ) { String[] securityList = new String[stackDir.Count]; stackDir.CopyTo(securityList, 0); for (int j = 0 ; j < securityList.Length; j++) securityList[j] += "\\."; // leaf will never have a slash at the end // Security check for all directories not present only. #if !FEATURE_PAL && FEATURE_MACL AccessControlActions control = (dirSecurity == null) ? AccessControlActions.None : AccessControlActions.Change; FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, control, securityList, false, false); #else #if FEATURE_CORECLR if (checkHost) { foreach (String demandPath in securityList) { FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, String.Empty, demandPath); state.EnsureState(); } } #else FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, securityList, false, false ); #endif #endif //!FEATURE_PAL && FEATURE_MACL } // If we were passed a DirectorySecurity, convert it to a security // descriptor and set it in he call to CreateDirectory. Win32Native.SECURITY_ATTRIBUTES secAttrs = null; #if FEATURE_MACL if (dirSecurity != null) { secAttrs = new Win32Native.SECURITY_ATTRIBUTES(); secAttrs.nLength = (int)Marshal.SizeOf(secAttrs); // For ACL's, get the security descriptor from the FileSecurity. byte[] sd = dirSecurity.GetSecurityDescriptorBinaryForm(); byte * bytesOnStack = stackalloc byte[sd.Length]; Buffer.Memcpy(bytesOnStack, 0, sd, 0, sd.Length); secAttrs.pSecurityDescriptor = bytesOnStack; } #endif bool r = true; int firstError = 0; String errorString = path; // If all the security checks succeeded create all the directories while (stackDir.Count > 0) { String name = stackDir[stackDir.Count - 1]; stackDir.RemoveAt(stackDir.Count - 1); if (PathInternal.IsDirectoryTooLong(name)) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); r = Win32Native.CreateDirectory(name, secAttrs); if (!r && (firstError == 0)) { int currentError = Marshal.GetLastWin32Error(); // While we tried to avoid creating directories that don't // exist above, there are at least two cases that will // cause us to see ERROR_ALREADY_EXISTS here. InternalExists // can fail because we didn't have permission to the // directory. Secondly, another thread or process could // create the directory between the time we check and the // time we try using the directory. Thirdly, it could // fail because the target does exist, but is a file. if (currentError != Win32Native.ERROR_ALREADY_EXISTS) firstError = currentError; else { // 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. if (File.InternalExists(name) || (!InternalExists(name, out currentError) && currentError == Win32Native.ERROR_ACCESS_DENIED)) { firstError = currentError; // Give the user a nice error message, but don't leak path information. try { CheckPermissions(string.Empty, name, checkHost: checkHost, access: FileSecurityStateAccess.PathDiscovery); errorString = name; } catch(SecurityException) {} } } } } // We need this check to mask OS differences // Handle CreateDirectory("X:\\foo") when X: doesn't exist. Similarly for n/w paths. if ((count == 0) && !somepathexists) { String root = InternalGetDirectoryRoot(fullPath); if (!InternalExists(root)) { // Extract the root from the passed in path again for security. __Error.WinIOError(Win32Native.ERROR_PATH_NOT_FOUND, InternalGetDirectoryRoot(path)); } return; } // Only throw an exception if creating the exact directory we // wanted failed to work correctly. if (!r && (firstError != 0)) { __Error.WinIOError(firstError, errorString); } } // Tests if the given path refers to an existing DirectoryInfo on disk. // // Your application must have Read permission to the directory's // contents. // [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static bool Exists(String path) { return InternalExistsHelper(path, checkHost: true); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static bool UnsafeExists(String path) { return InternalExistsHelper(path, checkHost: false); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static bool InternalExistsHelper(String path, bool checkHost) { if (path == null || path.Length == 0) return false; try { // This could be further optimized- there is no need to get the full path if we aren't checking our // own permissions- Windows will normalize it when we get the attributes. string fullPath = GetFullPathAndCheckPermissions(path, checkHost: checkHost); return InternalExists(fullPath); } catch (ArgumentException) { } catch (NotSupportedException) { } // Security can throw this on ":" catch (SecurityException) { } catch (IOException) { } catch (UnauthorizedAccessException) { #if !FEATURE_PAL Contract.Assert(false, "Ignore this assert and send a repro to Microsoft. This assert was tracking purposes only."); #endif //!FEATURE_PAL } return false; } // Determine whether path describes an existing directory // on disk, avoiding security checks. [System.Security.SecurityCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static bool InternalExists(String path) { int lastError = Win32Native.ERROR_SUCCESS; return InternalExists(path, out lastError); } // Determine whether path describes an existing directory // on disk, avoiding security checks. [System.Security.SecurityCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static bool InternalExists(String path, out int lastError) { Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); lastError = File.FillAttributeInfo(path, ref data, false, true); return (lastError == 0) && (data.fileAttributes != -1) && ((data.fileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) != 0); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static void SetCreationTime(String path,DateTime creationTime) { SetCreationTimeUtc(path, creationTime.ToUniversalTime()); } [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public unsafe static void SetCreationTimeUtc(String path,DateTime creationTimeUtc) { using (SafeFileHandle handle = Directory.OpenHandle(path)) { Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(creationTimeUtc.ToFileTimeUtc()); bool r = Win32Native.SetFileTime(handle, &fileTime, null, null); if (!r) { int errorCode = Marshal.GetLastWin32Error(); __Error.WinIOError(errorCode, path); } } } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static DateTime GetCreationTime(String path) { return File.GetCreationTime(path); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static DateTime GetCreationTimeUtc(String path) { return File.GetCreationTimeUtc(path); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static void SetLastWriteTime(String path,DateTime lastWriteTime) { SetLastWriteTimeUtc(path, lastWriteTime.ToUniversalTime()); } [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public unsafe static void SetLastWriteTimeUtc(String path,DateTime lastWriteTimeUtc) { using (SafeFileHandle handle = Directory.OpenHandle(path)) { Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(lastWriteTimeUtc.ToFileTimeUtc()); bool r = Win32Native.SetFileTime(handle, null, null, &fileTime); if (!r) { int errorCode = Marshal.GetLastWin32Error(); __Error.WinIOError(errorCode, path); } } } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static DateTime GetLastWriteTime(String path) { return File.GetLastWriteTime(path); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static DateTime GetLastWriteTimeUtc(String path) { return File.GetLastWriteTimeUtc(path); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static void SetLastAccessTime(String path,DateTime lastAccessTime) { SetLastAccessTimeUtc(path, lastAccessTime.ToUniversalTime()); } [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public unsafe static void SetLastAccessTimeUtc(String path,DateTime lastAccessTimeUtc) { using (SafeFileHandle handle = Directory.OpenHandle(path)) { Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(lastAccessTimeUtc.ToFileTimeUtc()); bool r = Win32Native.SetFileTime(handle, null, &fileTime, null); if (!r) { int errorCode = Marshal.GetLastWin32Error(); __Error.WinIOError(errorCode, path); } } } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static DateTime GetLastAccessTime(String path) { return File.GetLastAccessTime(path); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static DateTime GetLastAccessTimeUtc(String path) { return File.GetLastAccessTimeUtc(path); } #if FEATURE_MACL [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static DirectorySecurity GetAccessControl(String path) { return new DirectorySecurity(path, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static DirectorySecurity GetAccessControl(String path, AccessControlSections includeSections) { return new DirectorySecurity(path, includeSections); } [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static void SetAccessControl(String path, DirectorySecurity directorySecurity) { if (directorySecurity == null) throw new ArgumentNullException("directorySecurity"); Contract.EndContractBlock(); String fullPath = Path.GetFullPathInternal(path); directorySecurity.Persist(fullPath); } #endif #endif // !MONO // Returns an array of filenames in the DirectoryInfo specified by path [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static String[] GetFiles(String path) { if (path == null) throw new ArgumentNullException("path"); Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return InternalGetFiles(path, "*", SearchOption.TopDirectoryOnly); } // Returns an array of Files in the current DirectoryInfo matching the // given search pattern (ie, "*.txt"). [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static String[] GetFiles(String path, String searchPattern) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return InternalGetFiles(path, searchPattern, SearchOption.TopDirectoryOnly); } // Returns an array of Files in the current DirectoryInfo matching the // given search pattern (ie, "*.txt") and search option [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static String[] GetFiles(String path, String searchPattern, SearchOption searchOption) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return InternalGetFiles(path, searchPattern, searchOption); } // Returns an array of Files in the current DirectoryInfo matching the // given search pattern (ie, "*.txt") and search option [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static String[] InternalGetFiles(String path, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); Contract.Requires(searchPattern != null); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); return InternalGetFileDirectoryNames(path, path, searchPattern, true, false, searchOption, true); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static String[] UnsafeGetFiles(String path, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); Contract.Requires(searchPattern != null); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); return InternalGetFileDirectoryNames(path, path, searchPattern, true, false, searchOption, false); } // Returns an array of Directories in the current directory. [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static String[] GetDirectories(String path) { if (path == null) throw new ArgumentNullException("path"); Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return InternalGetDirectories(path, "*", SearchOption.TopDirectoryOnly); } // Returns an array of Directories in the current DirectoryInfo matching the // given search criteria (ie, "*.txt"). [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static String[] GetDirectories(String path, String searchPattern) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return InternalGetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly); } // Returns an array of Directories in the current DirectoryInfo matching the // given search criteria (ie, "*.txt"). [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static String[] GetDirectories(String path, String searchPattern, SearchOption searchOption) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return InternalGetDirectories(path, searchPattern, searchOption); } // Returns an array of Directories in the current DirectoryInfo matching the // given search criteria (ie, "*.txt"). [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static String[] InternalGetDirectories(String path, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); Contract.Requires(searchPattern != null); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); Contract.Ensures(Contract.Result() != null); return InternalGetFileDirectoryNames(path, path, searchPattern, false, true, searchOption, true); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static String[] UnsafeGetDirectories(String path, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); Contract.Requires(searchPattern != null); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); Contract.Ensures(Contract.Result() != null); return InternalGetFileDirectoryNames(path, path, searchPattern, false, true, searchOption, false); } // Returns an array of strongly typed FileSystemInfo entries in the path [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static String[] GetFileSystemEntries(String path) { if (path == null) throw new ArgumentNullException("path"); Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return InternalGetFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly); } // Returns an array of strongly typed FileSystemInfo entries in the path with the // given search criteria (ie, "*.txt"). We disallow .. as a part of the search criteria [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static String[] GetFileSystemEntries(String path, String searchPattern) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return InternalGetFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly); } // Returns an array of strongly typed FileSystemInfo entries in the path with the // given search criteria (ie, "*.txt"). We disallow .. as a part of the search criteria [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static String[] GetFileSystemEntries(String path, String searchPattern, SearchOption searchOption) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return InternalGetFileSystemEntries(path, searchPattern, searchOption); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static String[] InternalGetFileSystemEntries(String path, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); Contract.Requires(searchPattern != null); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); return InternalGetFileDirectoryNames(path, path, searchPattern, true, true, searchOption, true); } // Private class that holds search data that is passed around // in the heap based stack recursion internal sealed class SearchData { public SearchData(String fullPath, String userPath, SearchOption searchOption) { Contract.Requires(fullPath != null && fullPath.Length > 0); Contract.Requires(userPath != null && userPath.Length > 0); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); this.fullPath = fullPath; this.userPath = userPath; this.searchOption = searchOption; } public readonly string fullPath; // Fully qualified search path excluding the search criteria in the end (ex, c:\temp\bar\foo) public readonly string userPath; // User specified path (ex, bar\foo) public readonly SearchOption searchOption; } // Returns fully qualified user path of dirs/files that matches the search parameters. // For recursive search this method will search through all the sub dirs and execute // the given search criteria against every dir. // For all the dirs/files returned, it will then demand path discovery permission for // their parent folders (it will avoid duplicate permission checks) [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static String[] InternalGetFileDirectoryNames(String path, String userPathOriginal, String searchPattern, bool includeFiles, bool includeDirs, SearchOption searchOption, bool checkHost) { Contract.Requires(path != null); Contract.Requires(userPathOriginal != null); Contract.Requires(searchPattern != null); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); IEnumerable enble = FileSystemEnumerableFactory.CreateFileNameIterator( path, userPathOriginal, searchPattern, includeFiles, includeDirs, searchOption, checkHost); List fileList = new List(enble); return fileList.ToArray(); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static IEnumerable EnumerateDirectories(String path) { if (path == null) throw new ArgumentNullException("path"); Contract.EndContractBlock(); return InternalEnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static IEnumerable EnumerateDirectories(String path, String searchPattern) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); Contract.EndContractBlock(); return InternalEnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static IEnumerable EnumerateDirectories(String path, String searchPattern, SearchOption searchOption) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.EndContractBlock(); return InternalEnumerateDirectories(path, searchPattern, searchOption); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static IEnumerable InternalEnumerateDirectories(String path, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); Contract.Requires(searchPattern != null); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); return EnumerateFileSystemNames(path, searchPattern, searchOption, false, true); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static IEnumerable EnumerateFiles(String path) { if (path == null) throw new ArgumentNullException("path"); Contract.Ensures(Contract.Result>() != null); Contract.EndContractBlock(); return InternalEnumerateFiles(path, "*", SearchOption.TopDirectoryOnly); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static IEnumerable EnumerateFiles(String path, String searchPattern) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); Contract.Ensures(Contract.Result>() != null); Contract.EndContractBlock(); return InternalEnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static IEnumerable EnumerateFiles(String path, String searchPattern, SearchOption searchOption) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.Ensures(Contract.Result>() != null); Contract.EndContractBlock(); return InternalEnumerateFiles(path, searchPattern, searchOption); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static IEnumerable InternalEnumerateFiles(String path, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); Contract.Requires(searchPattern != null); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); Contract.Ensures(Contract.Result>() != null); return EnumerateFileSystemNames(path, searchPattern, searchOption, true, false); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static IEnumerable EnumerateFileSystemEntries(String path) { if (path == null) throw new ArgumentNullException("path"); Contract.Ensures(Contract.Result>() != null); Contract.EndContractBlock(); return InternalEnumerateFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static IEnumerable EnumerateFileSystemEntries(String path, String searchPattern) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); Contract.Ensures(Contract.Result>() != null); Contract.EndContractBlock(); return InternalEnumerateFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static IEnumerable EnumerateFileSystemEntries(String path, String searchPattern, SearchOption searchOption) { if (path == null) throw new ArgumentNullException("path"); if (searchPattern == null) throw new ArgumentNullException("searchPattern"); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.Ensures(Contract.Result>() != null); Contract.EndContractBlock(); return InternalEnumerateFileSystemEntries(path, searchPattern, searchOption); } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static IEnumerable InternalEnumerateFileSystemEntries(String path, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); Contract.Requires(searchPattern != null); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); Contract.Ensures(Contract.Result>() != null); return EnumerateFileSystemNames(path, searchPattern, searchOption, true, true); } private static IEnumerable EnumerateFileSystemNames(String path, String searchPattern, SearchOption searchOption, bool includeFiles, bool includeDirs) { Contract.Requires(path != null); Contract.Requires(searchPattern != null); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); Contract.Ensures(Contract.Result>() != null); return FileSystemEnumerableFactory.CreateFileNameIterator(path, path, searchPattern, includeFiles, includeDirs, searchOption, true); } #if !MONO // Retrieves the names of the logical drives on this machine in the // form "C:\". // // Your application must have System Info permission. // [System.Security.SecuritySafeCritical] // auto-generated public static String[] GetLogicalDrives() { Contract.Ensures(Contract.Result() != null); #pragma warning disable 618 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); #pragma warning restore 618 int drives = Win32Native.GetLogicalDrives(); if (drives==0) __Error.WinIOError(); uint d = (uint)drives; int count = 0; while (d != 0) { if (((int)d & 1) != 0) count++; d >>= 1; } String[] result = new String[count]; char[] root = new char[] {'A', ':', '\\'}; d = (uint)drives; count = 0; while (d != 0) { if (((int)d & 1) != 0) { result[count++] = new String(root); } d >>= 1; root[0]++; } return result; } [System.Security.SecuritySafeCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static string GetDirectoryRoot(string path) { if (path==null) throw new ArgumentNullException("path"); Contract.EndContractBlock(); string fullPath = Path.GetFullPathInternal(path); string root = fullPath.Substring(0, Path.GetRootLength(fullPath)); CheckPermissions(path, root, checkHost: true, access: FileSecurityStateAccess.PathDiscovery); return root; } internal static string InternalGetDirectoryRoot(string path) { if (path == null) return null; return path.Substring(0, Path.GetRootLength(path)); } /*===============================CurrentDirectory=============================== **Action: Provides a getter and setter for the current directory. The original ** current DirectoryInfo is the one from which the process was started. **Returns: The current DirectoryInfo (from the getter). Void from the setter. **Arguments: The current DirectoryInfo to which to switch to the setter. **Exceptions: ==============================================================================*/ [System.Security.SecuritySafeCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static String GetCurrentDirectory() { return InternalGetCurrentDirectory(true); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static String UnsafeGetCurrentDirectory() { return InternalGetCurrentDirectory(false); } [System.Security.SecuritySafeCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static string InternalGetCurrentDirectory(bool checkHost) { string currentDirectory = AppContextSwitches.UseLegacyPathHandling ? LegacyGetCurrentDirectory() : NewGetCurrentDirectory(); CheckPermissions(string.Empty, currentDirectory, checkHost: true, access: FileSecurityStateAccess.PathDiscovery); return currentDirectory; } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static string LegacyGetCurrentDirectory() { StringBuilder sb = StringBuilderCache.Acquire(PathInternal.MaxShortPath + 1); if (Win32Native.GetCurrentDirectory(sb.Capacity, sb) == 0) __Error.WinIOError(); string currentDirectory = sb.ToString(); // Note that if we have somehow put our command prompt into short // file name mode (ie, by running edlin or a DOS grep, etc), then // this will return a short file name. if (currentDirectory.IndexOf('~') >= 0) { int r = Win32Native.GetLongPathName(currentDirectory, sb, sb.Capacity); if (r == 0 || r >= PathInternal.MaxShortPath) { int errorCode = Marshal.GetLastWin32Error(); if (r >= PathInternal.MaxShortPath) errorCode = Win32Native.ERROR_FILENAME_EXCED_RANGE; if (errorCode != Win32Native.ERROR_FILE_NOT_FOUND && errorCode != Win32Native.ERROR_PATH_NOT_FOUND && errorCode != Win32Native.ERROR_INVALID_FUNCTION && // by design - enough said. errorCode != Win32Native.ERROR_ACCESS_DENIED) __Error.WinIOError(errorCode, string.Empty); } currentDirectory = sb.ToString(); } StringBuilderCache.Release(sb); return currentDirectory; } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static string NewGetCurrentDirectory() { using (StringBuffer buffer = new StringBuffer(PathInternal.MaxShortPath)) { uint result = 0; while ((result = Win32Native.GetCurrentDirectoryW(buffer.CharCapacity, buffer.GetHandle())) > buffer.CharCapacity) { // Reported size is greater than the buffer size. Increase the capacity. // The size returned includes the null only if more space is needed (this case). buffer.EnsureCharCapacity(result); } if (result == 0) { __Error.WinIOError(); } buffer.Length = result; if (buffer.Contains('~')) { return LongPathHelper.GetLongPathName(buffer); } return buffer.ToString(); } } #if FEATURE_CORECLR [System.Security.SecurityCritical] // auto-generated #else [System.Security.SecuritySafeCritical] #endif [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static void SetCurrentDirectory(String path) { if (path==null) throw new ArgumentNullException("value"); if (path.Length==0) throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty")); Contract.EndContractBlock(); if (PathInternal.IsPathTooLong(path)) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); // This will have some large effects on the rest of the runtime // and other appdomains in this process. Demand unmanaged code. #pragma warning disable 618 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); #pragma warning restore 618 String fulldestDirName = Path.GetFullPathInternal(path); if (!Win32Native.SetCurrentDirectory(fulldestDirName)) { // If path doesn't exist, this sets last error to 2 (File // not Found). LEGACY: This may potentially have worked correctly // on Win9x, maybe. int errorCode = Marshal.GetLastWin32Error(); if (errorCode == Win32Native.ERROR_FILE_NOT_FOUND) errorCode = Win32Native.ERROR_PATH_NOT_FOUND; __Error.WinIOError(errorCode, fulldestDirName); } } [System.Security.SecuritySafeCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static void Move(String sourceDirName,String destDirName) { InternalMove(sourceDirName, destDirName, true); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static void UnsafeMove(String sourceDirName, String destDirName) { InternalMove(sourceDirName, destDirName, false); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static void InternalMove(String sourceDirName, String destDirName, bool checkHost) { if (sourceDirName==null) throw new ArgumentNullException("sourceDirName"); if (sourceDirName.Length==0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "sourceDirName"); if (destDirName==null) throw new ArgumentNullException("destDirName"); if (destDirName.Length==0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destDirName"); Contract.EndContractBlock(); String fullsourceDirName = Path.GetFullPathInternal(sourceDirName); String sourcePath = GetDemandDir(fullsourceDirName, false); if (PathInternal.IsDirectoryTooLong(sourcePath)) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); String fulldestDirName = Path.GetFullPathInternal(destDirName); String destPath = GetDemandDir(fulldestDirName, false); if (PathInternal.IsDirectoryTooLong(sourcePath)) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); #if FEATURE_CORECLR if (checkHost) { FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Write | FileSecurityStateAccess.Read, sourceDirName, sourcePath); FileSecurityState destState = new FileSecurityState(FileSecurityStateAccess.Write, destDirName, destPath); sourceState.EnsureState(); destState.EnsureState(); } #else FileIOPermission.QuickDemand(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, sourcePath, false, false); FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, destPath, false, false); #endif if (String.Compare(sourcePath, destPath, StringComparison.OrdinalIgnoreCase) == 0) throw new IOException(Environment.GetResourceString("IO.IO_SourceDestMustBeDifferent")); String sourceRoot = Path.GetPathRoot(sourcePath); String destinationRoot = Path.GetPathRoot(destPath); if (String.Compare(sourceRoot, destinationRoot, StringComparison.OrdinalIgnoreCase) != 0) throw new IOException(Environment.GetResourceString("IO.IO_SourceDestMustHaveSameRoot")); if (!Win32Native.MoveFile(sourceDirName, destDirName)) { int hr = Marshal.GetLastWin32Error(); if (hr == Win32Native.ERROR_FILE_NOT_FOUND) // Source dir not found { hr = Win32Native.ERROR_PATH_NOT_FOUND; __Error.WinIOError(hr, fullsourceDirName); } // 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. if (hr == Win32Native.ERROR_ACCESS_DENIED) // WinNT throws IOException. This check is for Win9x. We can't change it for backcomp. throw new IOException(Environment.GetResourceString("UnauthorizedAccess_IODenied_Path", sourceDirName), Win32Native.MakeHRFromErrorCode(hr)); __Error.WinIOError(hr, String.Empty); } } [System.Security.SecuritySafeCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static void Delete(String path) { String fullPath = Path.GetFullPathInternal(path); Delete(fullPath, path, false, true); } [System.Security.SecuritySafeCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static void Delete(String path, bool recursive) { String fullPath = Path.GetFullPathInternal(path); Delete(fullPath, path, recursive, true); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static void UnsafeDelete(String path, bool recursive) { String fullPath = Path.GetFullPathInternal(path); Delete(fullPath, path, recursive, false); } // Called from DirectoryInfo as well. FullPath is fully qualified, // while the user path is used for feedback in exceptions. [System.Security.SecurityCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal static void Delete(String fullPath, String userPath, bool recursive, bool checkHost) { String demandPath; // If not recursive, do permission check only on this directory // else check for the whole directory structure rooted below demandPath = GetDemandDir(fullPath, thisDirOnly: !recursive); #if FEATURE_CORECLR if (checkHost) { FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, userPath, demandPath); state.EnsureState(); } #else // Make sure we have write permission to this directory FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, demandPath, false, false); #endif // Do not recursively delete through reparse points. Perhaps in a // future version we will add a new flag to control this behavior, // but for now we're much safer if we err on the conservative side. // This applies to symbolic links and mount points. Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); int dataInitialised = File.FillAttributeInfo(fullPath, ref data, false, true); if (dataInitialised != 0) { // Ensure we throw a DirectoryNotFoundException. if (dataInitialised == Win32Native.ERROR_FILE_NOT_FOUND) dataInitialised = Win32Native.ERROR_PATH_NOT_FOUND; __Error.WinIOError(dataInitialised, fullPath); } if (((FileAttributes)data.fileAttributes & FileAttributes.ReparsePoint) != 0) recursive = false; DeleteHelper(fullPath, userPath, recursive, true); } // Note that fullPath is fully qualified, while userPath may be // relative. Use userPath for all exception messages to avoid leaking // fully qualified path information. [System.Security.SecurityCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static void DeleteHelper(String fullPath, String userPath, bool recursive, bool throwOnTopLevelDirectoryNotFound) { bool r; int hr; Exception ex = null; // Do not recursively delete through reparse points. Perhaps in a // future version we will add a new flag to control this behavior, // but for now we're much safer if we err on the conservative side. // This applies to symbolic links and mount points. // Note the logic to check whether fullPath is a reparse point is // in Delete(String, String, bool), and will set "recursive" to false. // Note that Win32's DeleteFile and RemoveDirectory will just delete // the reparse point itself. if (recursive) { Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); // Open a Find handle using (SafeFindHandle hnd = Win32Native.FindFirstFile(fullPath+Path.DirectorySeparatorCharAsString+"*", data)) { if (hnd.IsInvalid) { hr = Marshal.GetLastWin32Error(); __Error.WinIOError(hr, fullPath); } do { bool isDir = (0!=(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)); if (isDir) { // Skip ".", "..". if (data.cFileName.Equals(".") || data.cFileName.Equals("..")) continue; // Recurse for all directories, unless they are // reparse points. Do not follow mount points nor // symbolic links, but do delete the reparse point // itself. bool shouldRecurse = (0 == (data.dwFileAttributes & (int) FileAttributes.ReparsePoint)); if (shouldRecurse) { String newFullPath = Path.InternalCombine(fullPath, data.cFileName); String newUserPath = Path.InternalCombine(userPath, data.cFileName); try { DeleteHelper(newFullPath, newUserPath, recursive, false); } catch(Exception e) { if (ex == null) { ex = e; } } } else { // Check to see if this is a mount point, and // unmount it. if (data.dwReserved0 == Win32Native.IO_REPARSE_TAG_MOUNT_POINT) { // Use full path plus a trailing '\' String mountPoint = Path.InternalCombine(fullPath, data.cFileName + Path.DirectorySeparatorChar); r = Win32Native.DeleteVolumeMountPoint(mountPoint); if (!r) { hr = Marshal.GetLastWin32Error(); if (hr != Win32Native.ERROR_PATH_NOT_FOUND) { try { __Error.WinIOError(hr, data.cFileName); } catch(Exception e) { if (ex == null) { ex = e; } } } } } // RemoveDirectory on a symbolic link will // remove the link itself. String reparsePoint = Path.InternalCombine(fullPath, data.cFileName); r = Win32Native.RemoveDirectory(reparsePoint); if (!r) { hr = Marshal.GetLastWin32Error(); if (hr != Win32Native.ERROR_PATH_NOT_FOUND) { try { __Error.WinIOError(hr, data.cFileName); } catch(Exception e) { if (ex == null) { ex = e; } } } } } } else { String fileName = Path.InternalCombine(fullPath, data.cFileName); r = Win32Native.DeleteFile(fileName); if (!r) { hr = Marshal.GetLastWin32Error(); if (hr != Win32Native.ERROR_FILE_NOT_FOUND) { try { __Error.WinIOError(hr, data.cFileName); } catch (Exception e) { if (ex == null) { ex = e; } } } } } } while (Win32Native.FindNextFile(hnd, data)); // Make sure we quit with a sensible error. hr = Marshal.GetLastWin32Error(); } if (ex != null) throw ex; if (hr!=0 && hr!=Win32Native.ERROR_NO_MORE_FILES) __Error.WinIOError(hr, userPath); } r = Win32Native.RemoveDirectory(fullPath); if (!r) { hr = Marshal.GetLastWin32Error(); if (hr == Win32Native.ERROR_FILE_NOT_FOUND) // A dubious error code. hr = Win32Native.ERROR_PATH_NOT_FOUND; // 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. if (hr == Win32Native.ERROR_ACCESS_DENIED) throw new IOException(Environment.GetResourceString("UnauthorizedAccess_IODenied_Path", userPath)); // don't throw the DirectoryNotFoundException since this is a subdir and there could be a ---- // between two Directory.Delete callers if (hr == Win32Native.ERROR_PATH_NOT_FOUND && !throwOnTopLevelDirectoryNotFound) return; __Error.WinIOError(hr, fullPath); } } [System.Security.SecurityCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static SafeFileHandle OpenHandle(String path) { String fullPath = Path.GetFullPathInternal(path); String root = Path.GetPathRoot(fullPath); if (root == fullPath && root[1] == Path.VolumeSeparatorChar) throw new ArgumentException(Environment.GetResourceString("Arg_PathIsVolume")); #if !FEATURE_CORECLR FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, GetDemandDir(fullPath, true), false, false); #endif SafeFileHandle handle = Win32Native.SafeCreateFile ( fullPath, GENERIC_WRITE, (FileShare) (FILE_SHARE_WRITE|FILE_SHARE_DELETE), null, FileMode.Open, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero ); if (handle.IsInvalid) { int hr = Marshal.GetLastWin32Error(); __Error.WinIOError(hr, fullPath); } return handle; } private const int FILE_ATTRIBUTE_DIRECTORY = 0x00000010; private const int GENERIC_WRITE = unchecked((int)0x40000000); private const int FILE_SHARE_WRITE = 0x00000002; private const int FILE_SHARE_DELETE = 0x00000004; private const int OPEN_EXISTING = 0x00000003; private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; #endif // !MONO } }