diff --git a/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs b/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs
index 5611e9c696987..3e3486157e841 100644
--- a/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs
+++ b/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs
@@ -12,47 +12,27 @@ namespace System.IO
/// Contains internal path helpers that are shared between many projects.
internal static partial class PathInternal
{
- private static readonly bool s_isCaseSensitive = GetIsCaseSensitive();
-
/// Returns a comparison that can be used to compare file and directory names for equality.
internal static StringComparison StringComparison
{
get
{
- return s_isCaseSensitive ?
+ return IsCaseSensitive ?
StringComparison.Ordinal :
StringComparison.OrdinalIgnoreCase;
}
}
/// Gets whether the system is case-sensitive.
- internal static bool IsCaseSensitive { get { return s_isCaseSensitive; } }
-
- ///
- /// Determines whether the file system is case sensitive.
- ///
- ///
- /// Ideally we'd use something like pathconf with _PC_CASE_SENSITIVE, but that is non-portable,
- /// not supported on Windows or Linux, etc. For now, this function creates a tmp file with capital letters
- /// and then tests for its existence with lower-case letters. This could return invalid results in corner
- /// cases where, for example, different file systems are mounted with differing sensitivities.
- ///
- private static bool GetIsCaseSensitive()
+ internal static bool IsCaseSensitive
{
- try
- {
- string pathWithUpperCase = Path.Combine(Path.GetTempPath(), "CASESENSITIVETEST" + Guid.NewGuid().ToString("N"));
- using (new FileStream(pathWithUpperCase, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose))
- {
- string lowerCased = pathWithUpperCase.ToLowerInvariant();
- return !File.Exists(lowerCased);
- }
- }
- catch
+ get
{
- // In case something goes wrong (e.g. temp pointing to a privilieged directory), we don't
- // want to fail just because of a casing test, so we assume case-insensitive-but-preserving.
- return false;
+#if MS_IO_REDIST
+ return false; // Windows is always case-insensitive
+#else
+ return !(OperatingSystem.IsWindows() || OperatingSystem.IsMacOS() || OperatingSystem.IsMacCatalyst() || OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS());
+#endif
}
}
}
diff --git a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs
index 2ba15d31b4144..d82346f6f4f10 100644
--- a/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs
+++ b/src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs
@@ -127,5 +127,24 @@ protected void ReadOnly_FileSystemHelper(Action testAction, string subDi
Assert.Equal(0, AdminHelpers.RunAsSudo($"umount {readOnlyDirectory}"));
}
}
+
+ ///
+ /// Determines whether the file system is case sensitive by creating a file in the specified folder and observing the result.
+ ///
+ ///
+ /// Ideally we'd use something like pathconf with _PC_CASE_SENSITIVE, but that is non-portable,
+ /// not supported on Windows or Linux, etc. For now, this function creates a tmp file with capital letters
+ /// and then tests for its existence with lower-case letters. This could return invalid results in corner
+ /// cases where, for example, different file systems are mounted with differing sensitivities.
+ ///
+ protected static bool GetIsCaseSensitiveByProbing(string probingDirectory)
+ {
+ string pathWithUpperCase = Path.Combine(probingDirectory, "CASESENSITIVETEST" + Guid.NewGuid().ToString("N"));
+ using (new FileStream(pathWithUpperCase, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose))
+ {
+ string lowerCased = pathWithUpperCase.ToLowerInvariant();
+ return !File.Exists(lowerCased);
+ }
+ }
}
}
diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj
index 628bbb4324aea..58669657e51b1 100644
--- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj
+++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj
@@ -37,6 +37,8 @@
+
diff --git a/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs b/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs
new file mode 100644
index 0000000000000..c908601862eea
--- /dev/null
+++ b/src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.IO.Tests
+{
+ public class PathInternalTests : FileSystemTest
+ {
+ [Fact]
+ [OuterLoop]
+ public void PathInternalIsCaseSensitiveMatchesProbing()
+ {
+ string probingDirectory = TestDirectory;
+ Assert.Equal(GetIsCaseSensitiveByProbing(probingDirectory), PathInternal.IsCaseSensitive);
+ }
+ }
+}
diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
index a271109ce2fc6..94cec86ce3a9a 100644
--- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
+++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
@@ -51,6 +51,7 @@
+
@@ -191,6 +192,8 @@
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
index ecc990b07c6e1..5a7f0c8d1bbe5 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
@@ -129,17 +129,5 @@ public static ReadOnlySpan GetPathRoot(ReadOnlySpan path)
return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString.AsSpan() : ReadOnlySpan.Empty;
}
- /// Gets whether the system is case-sensitive.
- internal static bool IsCaseSensitive
- {
- get
- {
- #if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
- return false;
- #else
- return true;
- #endif
- }
- }
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
index 5e641806926b8..5f4b8e11a1920 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
@@ -230,9 +230,6 @@ public static ReadOnlySpan GetPathRoot(ReadOnlySpan path)
return pathRoot <= 0 ? ReadOnlySpan.Empty : path.Slice(0, pathRoot);
}
- /// Gets whether the system is case-sensitive.
- internal static bool IsCaseSensitive => false;
-
///
/// Returns the volume name for dos, UNC and device paths.
///
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
index 7601e45e074d1..104704de7b715 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
@@ -860,7 +860,7 @@ private static unsafe void Populate83FileNameFromRandomBytes(byte* bytes, int by
/// Thrown if or is null or an empty string.
public static string GetRelativePath(string relativeTo, string path)
{
- return GetRelativePath(relativeTo, path, StringComparison);
+ return GetRelativePath(relativeTo, path, PathInternal.StringComparison);
}
private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType)
@@ -957,12 +957,6 @@ private static string GetRelativePath(string relativeTo, string path, StringComp
return sb.ToString();
}
- /// Returns a comparison that can be used to compare file and directory names for equality.
- internal static StringComparison StringComparison =>
- IsCaseSensitive ?
- StringComparison.Ordinal :
- StringComparison.OrdinalIgnoreCase;
-
///
/// Trims one trailing directory separator beyond the root of the path.
///
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
index 6c733ef6ca150..2a5a3c1da07fb 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
@@ -781,7 +781,7 @@ private static void OnAssemblyLoad(RuntimeAssembly assembly)
string assemblyPath = Path.Combine(parentDirectory, assemblyName.CultureName!, $"{assemblyName.Name}.dll");
bool exists = System.IO.FileSystem.FileExists(assemblyPath);
- if (!exists && Path.IsCaseSensitive)
+ if (!exists && PathInternal.IsCaseSensitive)
{
#if CORECLR
if (AssemblyLoadContext.IsTracingEnabled())