diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs index 835ab22793730c..89e3dda45bf434 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs @@ -85,6 +85,12 @@ public DirectoryInfo CreateSubdirectory(string path) ReadOnlySpan trimmedNewPath = Path.TrimEndingDirectorySeparator(newPath.AsSpan()); ReadOnlySpan trimmedCurrentPath = Path.TrimEndingDirectorySeparator(FullPath.AsSpan()); + // If trimmedCurrentPath still has a trailing separator (root directory case like "C:\" or "/"), + // remove it for proper boundary checking + if (trimmedCurrentPath.Length > 0 && PathInternal.IsDirectorySeparator(trimmedCurrentPath[trimmedCurrentPath.Length - 1])) + { + trimmedCurrentPath = trimmedCurrentPath.Slice(0, trimmedCurrentPath.Length - 1); + } // We want to make sure the requested directory is actually under the subdirectory. if (trimmedNewPath.StartsWith(trimmedCurrentPath, PathInternal.StringComparison) // Allow the exact same path, but prevent allowing "..\FooBar" through when the directory is "Foo" diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/EnumerableTests.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/EnumerableTests.cs index 20f61989594b09..ac0ddfb5236d0c 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/EnumerableTests.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/EnumerableTests.cs @@ -55,6 +55,75 @@ public void FileEnumeratorIsThreadSafe_ParallelForEach() } } + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public void CreateSubdirectory_RootDriveSubfolder_Windows() + { + // Get the root of the OS drive (e.g., "C:\") + string rootDrive = Path.GetPathRoot(Environment.SystemDirectory); + + // Create a DirectoryInfo for the root drive + DirectoryInfo rootDirectory = new DirectoryInfo(rootDrive); + + // Create a unique test folder name to avoid conflicts + string testFolderName = $"TestFolder_{Guid.NewGuid():N}"; + + try + { + // Create a subdirectory directly in the root + DirectoryInfo subDirectory = rootDirectory.CreateSubdirectory(testFolderName); + + // Verify it was created + Assert.True(subDirectory.Exists); + Assert.Equal(Path.Combine(rootDrive, testFolderName), subDirectory.FullName); + } + finally + { + // Cleanup + string testFolderPath = Path.Combine(rootDrive, testFolderName); + if (Directory.Exists(testFolderPath)) + { + Directory.Delete(testFolderPath, recursive: true); + } + } + } + + + [Fact] + [PlatformSpecific(TestPlatforms.Linux)] + public void CreateSubdirectory_RootDriveSubfolder_Linux() + { + // Get the root of the OS drive (e.g., "/") + // can not use string rootDrive = Path.GetPathRoot(Environment.SystemDirectory); as Environment.SystemDirectory is empty for Linux + string rootDrive = "/" + + // Create a DirectoryInfo for the root drive + DirectoryInfo rootDirectory = new DirectoryInfo(rootDrive); + + // Create a unique test folder name to avoid conflicts + string testFolderName = $"TestFolder_{Guid.NewGuid():N}"; + + try + { + // Create a subdirectory directly in the root + DirectoryInfo subDirectory = rootDirectory.CreateSubdirectory(testFolderName); + + // Verify it was created + Assert.True(subDirectory.Exists); + Assert.Equal(Path.Combine(rootDrive, testFolderName), subDirectory.FullName); + } + finally + { + // Cleanup + string testFolderPath = Path.Combine(rootDrive, testFolderName); + if (Directory.Exists(testFolderPath)) + { + Directory.Delete(testFolderPath, recursive: true); + } + } + } + + [Fact] public void EnumerateDirectories_NonBreakingSpace() {