diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index 059118499d23f3..55d3c7aa5ce695 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO.Enumeration; +using System.Runtime.CompilerServices; using System.Text; using Microsoft.Win32.SafeHandles; @@ -496,6 +497,8 @@ private static void RemoveDirectoryRecursive(string fullPath) { if (isDirectory) { + // Since this is a recursive call, we have to ensure that we have sufficient stack space. + RuntimeHelpers.EnsureSufficientExecutionStack(); RemoveDirectoryRecursive(childPath); } else diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs index cc29702789a71f..203f485774bcb2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.ComponentModel; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; @@ -344,6 +345,8 @@ private static void RemoveDirectoryRecursive(string fullPath, ref Interop.Kernel // Not a reparse point, or the reparse point isn't a name surrogate, recurse. try { + // Since this is a recursive call, we have to ensure that we have sufficient stack space. + RuntimeHelpers.EnsureSufficientExecutionStack(); RemoveDirectoryRecursive( Path.Combine(fullPath, fileName), findData: ref findData, diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete.cs index 2188cacd3df716..cbb64c5f66f79b 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete.cs @@ -288,6 +288,30 @@ public void RecursiveDelete_DeepNesting() Delete(rootDirectory, recursive: true); } + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public void RecursiveDelete_issue84344() + { + // Test the scenario described in the GitHub issue #84344 where the nesting depth of subdirectories is huge (14000). + // But this test will take a long time to run if we use 14000 as the depth, because the original depth number is too deep. + // Therefore we reduce the depth to 9000 for quick testing purpose but with a large enough number. + // See https://github.com/dotnet/runtime/issues/84344 + // Although the scenario on the original issue is done on Windows, + // we should be able to run this test on all platforms to test the recursive Directory.Delete(). + // Unfortunately, on Unix/Linux the max path length is limited to 4096, therefore this test is targeted to only runs on Windows. + DirectoryInfo testDir = new DirectoryInfo(GetTestFilePath()); + var depth = 9000; + + // Create a specific subdir only for this test. + DirectoryInfo subDir = testDir.CreateSubdirectory("test_issue84344"); + for (int i = 0; i < depth; i++) + { + subDir = subDir.CreateSubdirectory("a"); + } + // Now delete the subdir recursively. + Directory.Delete(subDir.FullName, recursive: true); + } + [Fact] [PlatformSpecific(TestPlatforms.Windows)] // Recursive delete throws IOException if directory contains in-use file public void RecursiveDelete_ShouldThrowIOExceptionIfContainedFileInUse()