private unsafe static void InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj) { #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 = LongPath.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)); } 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 #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 FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, securityList, false, false); #endif } // 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.MaxLongPath) { throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); } r = Win32Native.CreateDirectory(PathInternal.EnsureExtendedPrefix(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 (LongPathFile.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 { FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, GetDemandDir(name, true), false, false); 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); } }