Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
654d046
Initial plan
Copilot Oct 23, 2025
5e3bf96
Fix Directory.GetFiles() to not return file paths with trailing spaces
Copilot Oct 23, 2025
2a5b37c
Merge branch 'main' into copilot/fix-directory-getfiles-space-issue
jkotas Oct 23, 2025
e56dc76
Improve fix to preserve trailing separators while removing trailing s…
Copilot Oct 24, 2025
63f7b83
Revert to original simple fix and update test to match issue scenario
Copilot Oct 24, 2025
908c56c
Update src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/…
jkotas Oct 24, 2025
a9c933b
Apply suggestions from code review
jkotas Oct 24, 2025
f8ffe2a
Update src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/…
jkotas Oct 24, 2025
5457f04
Move fix to FileSystemEnumerableFactory and add OriginalRootDirectory…
Copilot Oct 24, 2025
e652606
Also trim trailing periods along with trailing spaces
Copilot Oct 24, 2025
9857edb
Update src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/…
jkotas Oct 24, 2025
5e3dbba
Update src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/…
jkotas Oct 24, 2025
dc2eeea
Delete OriginalRootDirectoryPreservesTrailingSpaces test
Copilot Oct 24, 2025
2bf80fc
Use WindowsTrailingProblematicFileNames for OriginalRootDirectory test
Copilot Oct 24, 2025
e66a19e
Make EnumerateDirectoryWithTrailingSpacePeriod Windows-specific and s…
Copilot Oct 25, 2025
89b835a
Fix OriginalRootDirectoryPreservesInput test to use trailing separato…
Copilot Oct 25, 2025
014ce4b
Skip trimming for extended path syntax (\\?\ and \\.\)
Copilot Oct 25, 2025
ac9a252
Enable EnumerateDirectoryWithTrailingSpacePeriod test on all platforms
Copilot Oct 25, 2025
5eb1705
Fix build error and make test Windows-specific
Copilot Oct 25, 2025
4e02f44
Make trailing space/period trimming Windows-specific with #if WINDOWS
Copilot Oct 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@ internal FileSystemEnumerator(string directory, bool isNormalized, EnumerationOp
{
ArgumentNullException.ThrowIfNull(directory);

_originalRootDirectory = directory;

string path = isNormalized ? directory : Path.GetFullPath(directory);
_originalRootDirectory = path;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jkotas, this will change behavior for FileSystemEntry.OriginalRootDirectory. Should we change it instead at the level of FileSystemEnumerable consumers?

internal static IEnumerable<string> UserFiles(string directory,
string expression,
EnumerationOptions options)
{
return new FileSystemEnumerable<string>(
directory,
(ref FileSystemEntry entry) => entry.ToSpecifiedFullPath(),
options)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
!entry.IsDirectory && MatchesPattern(expression, entry.FileName, options)
};
}
internal static IEnumerable<string> UserDirectories(string directory,
string expression,
EnumerationOptions options)
{
return new FileSystemEnumerable<string>(
directory,
(ref FileSystemEntry entry) => entry.ToSpecifiedFullPath(),
options)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
entry.IsDirectory && MatchesPattern(expression, entry.FileName, options)
};
}
internal static IEnumerable<string> UserEntries(string directory,
string expression,
EnumerationOptions options)
{
return new FileSystemEnumerable<string>(
directory,
(ref FileSystemEntry entry) => entry.ToSpecifiedFullPath(),
options)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
MatchesPattern(expression, entry.FileName, options)
};
}
internal static IEnumerable<FileInfo> FileInfos(
string directory,
string expression,
EnumerationOptions options,
bool isNormalized)
{
return new FileSystemEnumerable<FileInfo>(
directory,
(ref FileSystemEntry entry) => (FileInfo)entry.ToFileSystemInfo(),
options,
isNormalized)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
!entry.IsDirectory && MatchesPattern(expression, entry.FileName, options)
};
}
internal static IEnumerable<DirectoryInfo> DirectoryInfos(
string directory,
string expression,
EnumerationOptions options,
bool isNormalized)
{
return new FileSystemEnumerable<DirectoryInfo>(
directory,
(ref FileSystemEntry entry) => (DirectoryInfo)entry.ToFileSystemInfo(),
options,
isNormalized)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
entry.IsDirectory && MatchesPattern(expression, entry.FileName, options)
};
}
internal static IEnumerable<FileSystemInfo> FileSystemInfos(
string directory,
string expression,
EnumerationOptions options,
bool isNormalized)
{
return new FileSystemEnumerable<FileSystemInfo>(
directory,
(ref FileSystemEntry entry) => entry.ToFileSystemInfo(),
options,
isNormalized)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
MatchesPattern(expression, entry.FileName, options)
};
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we can instead just trim whitespace if there's no \\?\ prefix instead of Path.GetFullPath.

Copy link
Member

@jkotas jkotas Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will change behavior for FileSystemEntry.OriginalRootDirectory.

Hmmm, it does not help that we have exactly zero tests for this property.

@copilot Could you please address @jozkee's feedback? Also, add a test for OriginalRootDirectory property that would have caught this regression.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed @jozkee's feedback. Moved the fix to FileSystemEnumerableFactory.NormalizeInputs() to trim trailing spaces, preserving OriginalRootDirectory API behavior. Added tests for OriginalRootDirectory property that verify it preserves both trailing separators and trailing spaces (as it did before the change).

The fix now only affects Directory.* APIs through NormalizeInputs, not direct FileSystemEnumerator usage. Commit: 5457f04

_rootDirectory = Path.TrimEndingDirectorySeparator(path);
_options = options ?? EnumerationOptions.Default;
_remainingRecursionDepth = _options.MaxRecursionDepth;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,14 @@ public void WindowsEnumerateFilesWithTrailingSpacePeriod(string fileName)
[Theory]
[MemberData(nameof(TestData.WindowsTrailingProblematicFileNames), MemberType = typeof(TestData))]
[PlatformSpecific(TestPlatforms.Windows)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot This test does not seem to testing Windows specific behavior. Please enable the test on all platforms and make sure that it passed on all platforms

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enabled on all platforms. Updated FileSystemEnumerableFactory to use conditional compilation (#if WINDOWS) for the PathInternal.IsExtended check. On Unix, trailing spaces/periods are always trimmed since there's no extended path syntax concept. Commit: ac9a252

[ActiveIssue("https://github.com/dotnet/runtime/issues/113120")]
public void WindowsEnumerateDirectoryWithTrailingSpacePeriod(string dirName)
{
DirectoryInfo parentDir = Directory.CreateDirectory(GetTestFilePath());
string problematicDirPath = Path.Combine(parentDir.FullName, dirName);
Directory.CreateDirectory(@"\\?\" + problematicDirPath);
Directory.CreateDirectory(problematicDirPath);

string normalFileName = "normalfile.txt";
string filePath = Path.Combine(problematicDirPath, normalFileName);
string filePath = Path.Combine(Path.GetFullPath(problematicDirPath), normalFileName);
File.Create(filePath).Dispose();

string[] files = GetEntries(problematicDirPath);
Expand Down
Loading