Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change PathInternal.IsCaseSensitive to a constant #54340

Merged
merged 13 commits into from
Jun 23, 2021
Merged
36 changes: 8 additions & 28 deletions src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,47 +12,27 @@ namespace System.IO
/// <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
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions src/libraries/System.IO.FileSystem/tests/FileSystemTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,5 +127,24 @@ protected void ReadOnly_FileSystemHelper(Action<string> testAction, string subDi
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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
<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" />
Expand Down
18 changes: 18 additions & 0 deletions src/libraries/System.IO.FileSystem/tests/PathInternalTests.cs
Original file line number Diff line number Diff line change
@@ -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()
{
var probingDirectory = TestDirectory;
Assert.Equal(GetIsCaseSensitiveByProbing(probingDirectory), PathInternal.IsCaseSensitive);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<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" />
Expand Down Expand Up @@ -191,6 +192,8 @@
<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" />
Expand Down
12 changes: 0 additions & 12 deletions src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,17 +129,5 @@ public static ReadOnlySpan<char> GetPathRoot(ReadOnlySpan<char> path)
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
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,6 @@ public static ReadOnlySpan<char> GetPathRoot(ReadOnlySpan<char> path)
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>
Expand Down
8 changes: 1 addition & 7 deletions src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ private static unsafe void Populate83FileNameFromRandomBytes(byte* bytes, int by
/// <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)
Expand Down Expand Up @@ -957,12 +957,6 @@ private static string GetRelativePath(string relativeTo, string path, StringComp
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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down