/// <summary>Contains internal path helpers that are shared between many projects.</summary>
internal static partial class PathInternal
{
- private static readonly bool s_isCaseSensitive = GetIsCaseSensitive();
-
/// <summary>Returns a comparison that can be used to compare file and directory names for equality.</summary>
internal static StringComparison StringComparison
{
get
{
- return s_isCaseSensitive ?
+ return IsCaseSensitive ?
StringComparison.Ordinal :
StringComparison.OrdinalIgnoreCase;
}
}
/// <summary>Gets whether the system is case-sensitive.</summary>
- internal static bool IsCaseSensitive { get { return s_isCaseSensitive; } }
-
- /// <summary>
- /// Determines whether the file system is case sensitive.
- /// </summary>
- /// <remarks>
- /// 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.
- /// </remarks>
- 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
}
}
}
Assert.Equal(0, AdminHelpers.RunAsSudo($"umount {readOnlyDirectory}"));
}
}
+
+ /// <summary>
+ /// Determines whether the file system is case sensitive by creating a file in the specified folder and observing the result.
+ /// </summary>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ 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);
+ }
+ }
}
}
<Compile Include="$(CommonTestPath)System\IO\TempFile.cs" Link="Common\System\IO\TempFile.cs" />
<Compile Include="$(CommonTestPath)System\IO\PathFeatures.cs" Link="Common\System\IO\PathFeatures.cs" />
<Content Include="..\DirectoryInfo\test-dir\dummy.txt" Link="test-dir\dummy.txt" />
+ <Compile Include="$(CommonPath)System\IO\PathInternal.CaseSensitivity.cs"
+ Link="Common\System\IO\PathInternal.CaseSensitivity.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(CommonTestPath)StreamConformanceTests\StreamConformanceTests.csproj" />
--- /dev/null
+// 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);
+ }
+ }
+}
<Compile Include="Enumeration\ExampleTests.cs" />
<Compile Include="Enumeration\RemovedDirectoryTests.cs" />
<Compile Include="Enumeration\SymbolicLinksTests.cs" />
+ <Compile Include="PathInternalTests.cs" />
<Compile Include="RandomAccess\Base.cs" />
<Compile Include="RandomAccess\GetLength.cs" />
<Compile Include="RandomAccess\Read.cs" />
<Compile Include="$(CommonTestPath)System\IO\TempFile.cs" Link="Common\System\IO\TempFile.cs" />
<Compile Include="$(CommonTestPath)System\IO\PathFeatures.cs" Link="Common\System\IO\PathFeatures.cs" />
<Content Include="DirectoryInfo\test-dir\dummy.txt" Link="test-dir\dummy.txt" />
+ <Compile Include="$(CommonPath)System\IO\PathInternal.CaseSensitivity.cs"
+ Link="Common\System\IO\PathInternal.CaseSensitivity.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(CommonTestPath)StreamConformanceTests\StreamConformanceTests.csproj" />
return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString.AsSpan() : ReadOnlySpan<char>.Empty;
}
- /// <summary>Gets whether the system is case-sensitive.</summary>
- internal static bool IsCaseSensitive
- {
- get
- {
- #if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
- return false;
- #else
- return true;
- #endif
- }
- }
}
}
return pathRoot <= 0 ? ReadOnlySpan<char>.Empty : path.Slice(0, pathRoot);
}
- /// <summary>Gets whether the system is case-sensitive.</summary>
- internal static bool IsCaseSensitive => false;
-
/// <summary>
/// Returns the volume name for dos, UNC and device paths.
/// </summary>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="relativeTo"/> or <paramref name="path"/> is <c>null</c> or an empty string.</exception>
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)
return sb.ToString();
}
- /// <summary>Returns a comparison that can be used to compare file and directory names for equality.</summary>
- internal static StringComparison StringComparison =>
- IsCaseSensitive ?
- StringComparison.Ordinal :
- StringComparison.OrdinalIgnoreCase;
-
/// <summary>
/// Trims one trailing directory separator beyond the root of the path.
/// </summary>
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())