diff options
Diffstat (limited to 'src/mscorlib/src/System/IO/Directory.cs')
-rw-r--r-- | src/mscorlib/src/System/IO/Directory.cs | 1354 |
1 files changed, 1354 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/IO/Directory.cs b/src/mscorlib/src/System/IO/Directory.cs new file mode 100644 index 0000000000..4b43dbb1bf --- /dev/null +++ b/src/mscorlib/src/System/IO/Directory.cs @@ -0,0 +1,1354 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*============================================================ +** +** +** +** +** +** 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 class Directory { + 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] + 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] + 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] + internal static DirectoryInfo InternalCreateDirectoryHelper(String path, bool checkHost) + { + Contract.Requires(path != null); + Contract.Requires(path.Length != 0); + + String fullPath = Path.GetFullPathInternal(path); + + // 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. + String demandDir = GetDemandDir(fullPath, true); + +#if FEATURE_CORECLR + if (checkHost) + { + FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, path, demandDir); + state.EnsureState(); // do the check on the AppDomainManager to make sure this is allowed + } +#else + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandDir, false, false); +#endif + + InternalCreateDirectory(fullPath, path, null, checkHost); + + return new DirectoryInfo(fullPath, false); + } + +#if FEATURE_MACL + [System.Security.SecuritySafeCritical] // auto-generated + 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 = Path.GetFullPathInternal(path); + + // 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. + String demandDir = GetDemandDir(fullPath, true); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandDir, false, false ); + + 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. + 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; + } + + internal static void InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj) + { + InternalCreateDirectory(fullPath, path, dirSecurityObj, false); + } + + + [System.Security.SecuritySafeCritical] + 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<string> stackDir = new List<string>(); + + // 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) + { + 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_MACL + AccessControlActions control = (dirSecurity == null) ? AccessControlActions.None : AccessControlActions.Change; + new FileIOPermission(FileIOPermissionAccess.Write, control, securityList, false, false ).Demand(); +#else +#if FEATURE_CORECLR + if (checkHost) + { + foreach (String demandPath in securityList) + { + FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, String.Empty, demandPath); + state.EnsureState(); + } + } +#else + new FileIOPermission(FileIOPermissionAccess.Write, securityList, false, false ).Demand(); +#endif +#endif //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 (name.Length >= Path.MAX_DIRECTORY_PATH) + 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 { +#if FEATURE_CORECLR + if (checkHost) + { + FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, GetDemandDir(name, true)); + state.EnsureState(); + } +#else + new FileIOPermission(FileIOPermissionAccess.PathDiscovery, GetDemandDir(name, true)).Demand(); +#endif // FEATURE_CORECLR + 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 + public static bool Exists(String path) + { + return InternalExistsHelper(path, true); + } + + [System.Security.SecurityCritical] + internal static bool UnsafeExists(String path) + { + return InternalExistsHelper(path, false); + } + + [System.Security.SecurityCritical] + internal static bool InternalExistsHelper(String path, bool checkHost) { + try + { + if (path == null) + return false; + if (path.Length == 0) + return false; + + // Get fully qualified file name ending in \* for security check + + String fullPath = Path.GetFullPathInternal(path); + String demandPath = GetDemandDir(fullPath, true); + +#if FEATURE_CORECLR + if (checkHost) + { + FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, path, demandPath); + state.EnsureState(); + } +#else + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandPath, false, false); +#endif + + + return InternalExists(fullPath); + } + catch (ArgumentException) { } + catch (NotSupportedException) { } // Security can throw this on ":" + catch (SecurityException) { } + catch (IOException) { } + catch (UnauthorizedAccessException) + { + Contract.Assert(false, "Ignore this assert and send a repro to Microsoft. This assert was tracking purposes only."); + } + return false; + } + + // Determine whether path describes an existing directory + // on disk, avoiding security checks. + [System.Security.SecurityCritical] // auto-generated + 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 + 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); + } + + public static void SetCreationTime(String path,DateTime creationTime) + { + SetCreationTimeUtc(path, creationTime.ToUniversalTime()); + } + + [System.Security.SecuritySafeCritical] // auto-generated + 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); + } + } + } + + public static DateTime GetCreationTime(String path) + { + return File.GetCreationTime(path); + } + + public static DateTime GetCreationTimeUtc(String path) + { + return File.GetCreationTimeUtc(path); + } + + public static void SetLastWriteTime(String path,DateTime lastWriteTime) + { + SetLastWriteTimeUtc(path, lastWriteTime.ToUniversalTime()); + } + + [System.Security.SecuritySafeCritical] // auto-generated + 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); + } + } + } + + public static DateTime GetLastWriteTime(String path) + { + return File.GetLastWriteTime(path); + } + + public static DateTime GetLastWriteTimeUtc(String path) + { + return File.GetLastWriteTimeUtc(path); + } + + public static void SetLastAccessTime(String path,DateTime lastAccessTime) + { + SetLastAccessTimeUtc(path, lastAccessTime.ToUniversalTime()); + } + + [System.Security.SecuritySafeCritical] // auto-generated + 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); + } + } + } + + public static DateTime GetLastAccessTime(String path) + { + return File.GetLastAccessTime(path); + } + + public static DateTime GetLastAccessTimeUtc(String path) + { + return File.GetLastAccessTimeUtc(path); + } + +#if FEATURE_MACL + public static DirectorySecurity GetAccessControl(String path) + { + return new DirectorySecurity(path, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); + } + + public static DirectorySecurity GetAccessControl(String path, AccessControlSections includeSections) + { + return new DirectorySecurity(path, includeSections); + } + + [System.Security.SecuritySafeCritical] // auto-generated + 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 + + // Returns an array of filenames in the DirectoryInfo specified by path + public static String[] GetFiles(String path) + { + if (path == null) + throw new ArgumentNullException("path"); + Contract.Ensures(Contract.Result<String[]>() != null); + Contract.EndContractBlock(); + + return InternalGetFiles(path, "*", SearchOption.TopDirectoryOnly); + } + + // Returns an array of Files in the current DirectoryInfo matching the + // given search pattern (ie, "*.txt"). + 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<String[]>() != 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 + 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<String[]>() != 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 + 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] + 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. + public static String[] GetDirectories(String path) + { + if (path == null) + throw new ArgumentNullException("path"); + Contract.Ensures(Contract.Result<String[]>() != null); + Contract.EndContractBlock(); + + return InternalGetDirectories(path, "*", SearchOption.TopDirectoryOnly); + } + + // Returns an array of Directories in the current DirectoryInfo matching the + // given search criteria (ie, "*.txt"). + 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<String[]>() != 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"). + 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<String[]>() != null); + Contract.EndContractBlock(); + + return InternalGetDirectories(path, searchPattern, searchOption); + } + + // Returns an array of Directories in the current DirectoryInfo matching the + // given search criteria (ie, "*.txt"). + 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<String[]>() != null); + + return InternalGetFileDirectoryNames(path, path, searchPattern, false, true, searchOption, true); + } + + [System.Security.SecurityCritical] + 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<String[]>() != null); + + return InternalGetFileDirectoryNames(path, path, searchPattern, false, true, searchOption, false); + } + + // Returns an array of strongly typed FileSystemInfo entries in the path + public static String[] GetFileSystemEntries(String path) + { + if (path == null) + throw new ArgumentNullException("path"); + Contract.Ensures(Contract.Result<String[]>() != 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 + 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<String[]>() != 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 + 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<String[]>() != null); + Contract.EndContractBlock(); + + return InternalGetFileSystemEntries(path, searchPattern, searchOption); + } + + 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) + 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<String> enble = FileSystemEnumerableFactory.CreateFileNameIterator( + path, userPathOriginal, searchPattern, + includeFiles, includeDirs, searchOption, checkHost); + List<String> fileList = new List<String>(enble); + return fileList.ToArray(); + } + + public static IEnumerable<String> EnumerateDirectories(String path) + { + if (path == null) + throw new ArgumentNullException("path"); + Contract.EndContractBlock(); + + return InternalEnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly); + } + + public static IEnumerable<String> 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); + } + + public static IEnumerable<String> 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); + } + + private static IEnumerable<String> 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); + } + + public static IEnumerable<String> EnumerateFiles(String path) + { + if (path == null) + throw new ArgumentNullException("path"); + Contract.Ensures(Contract.Result<IEnumerable<String>>() != null); + Contract.EndContractBlock(); + + return InternalEnumerateFiles(path, "*", SearchOption.TopDirectoryOnly); + } + + public static IEnumerable<String> EnumerateFiles(String path, String searchPattern) + { + if (path == null) + throw new ArgumentNullException("path"); + if (searchPattern == null) + throw new ArgumentNullException("searchPattern"); + Contract.Ensures(Contract.Result<IEnumerable<String>>() != null); + Contract.EndContractBlock(); + + return InternalEnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly); + } + + public static IEnumerable<String> 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<IEnumerable<String>>() != null); + Contract.EndContractBlock(); + + return InternalEnumerateFiles(path, searchPattern, searchOption); + } + + private static IEnumerable<String> 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<IEnumerable<String>>() != null); + + return EnumerateFileSystemNames(path, searchPattern, searchOption, true, false); + } + + public static IEnumerable<String> EnumerateFileSystemEntries(String path) + { + if (path == null) + throw new ArgumentNullException("path"); + Contract.Ensures(Contract.Result<IEnumerable<String>>() != null); + Contract.EndContractBlock(); + + return InternalEnumerateFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly); + } + + public static IEnumerable<String> EnumerateFileSystemEntries(String path, String searchPattern) + { + if (path == null) + throw new ArgumentNullException("path"); + if (searchPattern == null) + throw new ArgumentNullException("searchPattern"); + Contract.Ensures(Contract.Result<IEnumerable<String>>() != null); + Contract.EndContractBlock(); + + return InternalEnumerateFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly); + } + + public static IEnumerable<String> 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<IEnumerable<String>>() != null); + Contract.EndContractBlock(); + + return InternalEnumerateFileSystemEntries(path, searchPattern, searchOption); + } + + private static IEnumerable<String> 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<IEnumerable<String>>() != null); + + return EnumerateFileSystemNames(path, searchPattern, searchOption, true, true); + } + + private static IEnumerable<String> 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<IEnumerable<String>>() != null); + + return FileSystemEnumerableFactory.CreateFileNameIterator(path, path, searchPattern, + includeFiles, includeDirs, searchOption, true); + } + + // 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<String[]>() != 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] + 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)); + String demandPath = GetDemandDir(root, true); + +#if FEATURE_CORECLR + FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, path, demandPath); + state.EnsureState(); +#else + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPath, false, false); +#endif + + 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] + public static String GetCurrentDirectory() + { + return InternalGetCurrentDirectory(true); + } + + [System.Security.SecurityCritical] + internal static String UnsafeGetCurrentDirectory() + { + return InternalGetCurrentDirectory(false); + } + + [System.Security.SecurityCritical] + private static String InternalGetCurrentDirectory(bool checkHost) + { + StringBuilder sb = StringBuilderCache.Acquire(Path.MAX_PATH + 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 >= Path.MAX_PATH) { + int errorCode = Marshal.GetLastWin32Error(); + if (r >= Path.MAX_PATH) + 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); + String demandPath = GetDemandDir(currentDirectory, true); + + +#if FEATURE_CORECLR + if (checkHost) + { + FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPath); + state.EnsureState(); + } +#else + new FileIOPermission( FileIOPermissionAccess.PathDiscovery, new String[] { demandPath }, false, false ).Demand(); +#endif + return currentDirectory; + } + + + #if FEATURE_CORECLR + [System.Security.SecurityCritical] // auto-generated + #else + [System.Security.SecuritySafeCritical] + #endif + 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 (path.Length >= Path.MAX_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] + public static void Move(String sourceDirName,String destDirName) { + InternalMove(sourceDirName, destDirName, true); + } + + [System.Security.SecurityCritical] + internal static void UnsafeMove(String sourceDirName,String destDirName) { + InternalMove(sourceDirName, destDirName, false); + } + + [System.Security.SecurityCritical] + 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 (sourcePath.Length >= Path.MAX_DIRECTORY_PATH) + throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); + + String fulldestDirName = Path.GetFullPathInternal(destDirName); + String destPath = GetDemandDir(fulldestDirName, false); + + if (destPath.Length >= Path.MAX_DIRECTORY_PATH) + 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] + public static void Delete(String path) + { + String fullPath = Path.GetFullPathInternal(path); + Delete(fullPath, path, false, true); + } + + [System.Security.SecuritySafeCritical] + public static void Delete(String path, bool recursive) + { + String fullPath = Path.GetFullPathInternal(path); + Delete(fullPath, path, recursive, true); + } + + [System.Security.SecurityCritical] + 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 + 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, !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 + new FileIOPermission(FileIOPermissionAccess.Write, new String[] { demandPath }, false, false ).Demand(); +#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 + 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 race condition + // between two Directory.Delete callers + if (hr == Win32Native.ERROR_PATH_NOT_FOUND && !throwOnTopLevelDirectoryNotFound) + return; + + __Error.WinIOError(hr, fullPath); + } + } + + // WinNT only. Win9x this code will not work. + [System.Security.SecurityCritical] // auto-generated + 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; + } + +} + |