From: Jeremy Kuhne Date: Thu, 15 Feb 2018 06:49:17 +0000 (-0800) Subject: Cleaning out dead code (dotnet/corefx#27150) X-Git-Tag: submit/tizen/20210909.063632~11031^2~5496 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8dc5e673847cee86d111fcdab82fb0e6f6882ac5;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Cleaning out dead code (dotnet/corefx#27150) Also refactor Unix attribute handling a bit to be more consistent and ensure that we don't make unnecessary addtional filesystem calls. Commit migrated from https://github.com/dotnet/corefx/commit/16f26ba855f5cc6d52a738e8bc3856c6f77cf761 --- diff --git a/src/libraries/Common/src/Interop/Windows/kernel32/Interop.GetLongPathName.cs b/src/libraries/Common/src/Interop/Windows/kernel32/Interop.GetLongPathName.cs index 185f0c77..1030a57 100644 --- a/src/libraries/Common/src/Interop/Windows/kernel32/Interop.GetLongPathName.cs +++ b/src/libraries/Common/src/Interop/Windows/kernel32/Interop.GetLongPathName.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.IO; using System.Runtime.InteropServices; using System.Text; @@ -11,15 +10,9 @@ internal partial class Interop internal partial class Kernel32 { /// - /// WARNING: This method does not implicitly handle long paths. Use GetLongPathName. + /// WARNING: This method does not implicitly handle long paths. /// [DllImport(Libraries.Kernel32, EntryPoint = "GetLongPathNameW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = false)] - private static extern int GetLongPathNamePrivate(string path, [Out]StringBuilder longPathBuffer, int bufferLength); - - internal static int GetLongPathName(string path, [Out]StringBuilder longPathBuffer, int bufferLength) - { - path = PathInternal.EnsureExtendedPrefixIfNeeded(path); - return GetLongPathNamePrivate(path, longPathBuffer, bufferLength); - } + internal static extern int GetLongPathName(string path, [Out]StringBuilder longPathBuffer, int bufferLength); } } diff --git a/src/libraries/Common/src/System/IO/PathInternal.Unix.cs b/src/libraries/Common/src/System/IO/PathInternal.Unix.cs index 27424cb..8e27f4b 100644 --- a/src/libraries/Common/src/System/IO/PathInternal.Unix.cs +++ b/src/libraries/Common/src/System/IO/PathInternal.Unix.cs @@ -11,12 +11,6 @@ namespace System.IO /// Contains internal path helpers that are shared between many projects. internal static partial class PathInternal { - // There is only one invalid path character in Unix - private const char InvalidPathChar = '\0'; - internal static char[] GetInvalidPathChars() => new char[] { InvalidPathChar }; - - internal const string ParentDirectoryPrefix = @"../"; - internal static int GetRootLength(ReadOnlySpan path) { return path.Length > 0 && IsDirectorySeparator(path[0]) ? 1 : 0; @@ -30,57 +24,6 @@ namespace System.IO return c == Path.DirectorySeparatorChar; } - /// - /// Normalize separators in the given path. Compresses forward slash runs. - /// - internal static string NormalizeDirectorySeparators(string path) - { - if (string.IsNullOrEmpty(path)) return path; - - // Make a pass to see if we need to normalize so we can potentially skip allocating - bool normalized = true; - - for (int i = 0; i < path.Length; i++) - { - if (IsDirectorySeparator(path[i]) - && (i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))) - { - normalized = false; - break; - } - } - - if (normalized) return path; - - StringBuilder builder = new StringBuilder(path.Length); - - for (int i = 0; i < path.Length; i++) - { - char current = path[i]; - - // Skip if we have another separator following - if (IsDirectorySeparator(current) - && (i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))) - continue; - - builder.Append(current); - } - - return builder.ToString(); - } - - /// - /// Returns true if the character is a directory or volume separator. - /// - /// The character to test. - internal static bool IsDirectoryOrVolumeSeparator(char ch) - { - // The directory separator, volume separator, and the alternate directory - // separator should be the same on Unix, so we only need to check one. - Debug.Assert(Path.DirectorySeparatorChar == Path.AltDirectorySeparatorChar); - Debug.Assert(Path.DirectorySeparatorChar == Path.VolumeSeparatorChar); - return ch == Path.DirectorySeparatorChar; - } internal static bool IsPartiallyQualified(string path) { diff --git a/src/libraries/Common/src/System/IO/PathInternal.Windows.cs b/src/libraries/Common/src/System/IO/PathInternal.Windows.cs index 7307969..6bc6a0f 100644 --- a/src/libraries/Common/src/System/IO/PathInternal.Windows.cs +++ b/src/libraries/Common/src/System/IO/PathInternal.Windows.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.Runtime.CompilerServices; -using System.Text; namespace System.IO { @@ -40,38 +38,16 @@ namespace System.IO // Local and Global MS-DOS Device Names // https://msdn.microsoft.com/en-us/library/windows/hardware/ff554302.aspx - internal const string ExtendedPathPrefix = @"\\?\"; + internal const string ExtendedDevicePathPrefix = @"\\?\"; internal const string UncPathPrefix = @"\\"; - internal const string UncExtendedPrefixToInsert = @"?\UNC\"; + internal const string UncDevicePrefixToInsert = @"?\UNC\"; internal const string UncExtendedPathPrefix = @"\\?\UNC\"; internal const string DevicePathPrefix = @"\\.\"; - internal const string ParentDirectoryPrefix = @"..\"; internal const int MaxShortPath = 260; - internal const int MaxShortDirectoryPath = 248; - internal const int MaxLongPath = short.MaxValue; + // \\?\, \\.\, \??\ internal const int DevicePrefixLength = 4; - // \\ - internal const int UncPrefixLength = 2; - // \\?\UNC\, \\.\UNC\ - internal const int UncExtendedPrefixLength = 8; - - internal static char[] GetInvalidPathChars() => new char[] - { - '|', '\0', - (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, - (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, - (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, - (char)31 - }; - - // [MS - FSA] 2.1.4.4 Algorithm for Determining if a FileName Is in an Expression - // https://msdn.microsoft.com/en-us/library/ff469270.aspx - private static readonly char[] s_wildcardChars = - { - '\"', '<', '>', '*', '?' - }; /// /// Returns true if the given character is a valid drive letter @@ -129,9 +105,9 @@ namespace System.IO // Given \\server\share in longpath becomes \\?\UNC\server\share if (path.StartsWith(UncPathPrefix, StringComparison.OrdinalIgnoreCase)) - return path.Insert(2, PathInternal.UncExtendedPrefixToInsert); + return path.Insert(2, UncDevicePrefixToInsert); - return PathInternal.ExtendedPathPrefix + path; + return ExtendedDevicePathPrefix + path; } /// @@ -168,85 +144,57 @@ namespace System.IO && path[3] == '\\'; } - /// - /// Check for known wildcard characters. '*' and '?' are the most common ones. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe bool HasWildCardCharacters(string path) - { - // Question mark is part of dos device syntax so we have to skip if we are - int startIndex = PathInternal.IsDevice(path) ? ExtendedPathPrefix.Length : 0; - - return path.IndexOfAny(s_wildcardChars, startIndex) >= 0; - } - +#if !NOSPAN /// /// Gets the length of the root of the path (drive, share, etc.). /// - internal static unsafe int GetRootLength(string path) - { - fixed(char* value = path) - { - return (int)GetRootLength(value, (uint)path.Length); - } - } - - private static unsafe uint GetRootLength(char* path, uint pathLength) + internal static int GetRootLength(ReadOnlySpan path) { - uint i = 0; - uint volumeSeparatorLength = 2; // Length to the colon "C:" - uint uncRootLength = 2; // Length to the start of the server name "\\" + int i = 0; + int volumeSeparatorLength = 2; // Length to the colon "C:" + int uncRootLength = 2; // Length to the start of the server name "\\" - bool extendedSyntax = StartsWithOrdinal(path, pathLength, ExtendedPathPrefix); - bool extendedUncSyntax = StartsWithOrdinal(path, pathLength, UncExtendedPathPrefix); + bool extendedSyntax = path.StartsWith(ExtendedDevicePathPrefix); + bool extendedUncSyntax = path.StartsWith(UncExtendedPathPrefix); if (extendedSyntax) { // Shift the position we look for the root from to account for the extended prefix if (extendedUncSyntax) { // "\\" -> "\\?\UNC\" - uncRootLength = (uint)UncExtendedPathPrefix.Length; + uncRootLength = UncExtendedPathPrefix.Length; } else { // "C:" -> "\\?\C:" - volumeSeparatorLength += (uint)ExtendedPathPrefix.Length; + volumeSeparatorLength += ExtendedDevicePathPrefix.Length; } } - if ((!extendedSyntax || extendedUncSyntax) && pathLength > 0 && IsDirectorySeparator(path[0])) + if ((!extendedSyntax || extendedUncSyntax) && path.Length > 0 && IsDirectorySeparator(path[0])) { // UNC or simple rooted path (e.g. "\foo", NOT "\\?\C:\foo") i = 1; // Drive rooted (\foo) is one character - if (extendedUncSyntax || (pathLength > 1 && IsDirectorySeparator(path[1]))) + if (extendedUncSyntax || (path.Length > 1 && IsDirectorySeparator(path[1]))) { // UNC (\\?\UNC\ or \\), scan past the next two directory separators at most // (e.g. to \\?\UNC\Server\Share or \\Server\Share\) i = uncRootLength; int n = 2; // Maximum separators to skip - while (i < pathLength && (!IsDirectorySeparator(path[i]) || --n > 0)) i++; + while (i < path.Length && (!IsDirectorySeparator(path[i]) || --n > 0)) i++; } } - else if (pathLength >= volumeSeparatorLength && path[volumeSeparatorLength - 1] == Path.VolumeSeparatorChar) + else if (path.Length >= volumeSeparatorLength && path[volumeSeparatorLength - 1] == Path.VolumeSeparatorChar) { // Path is at least longer than where we expect a colon, and has a colon (\\?\A:, A:) // If the colon is followed by a directory separator, move past it i = volumeSeparatorLength; - if (pathLength >= volumeSeparatorLength + 1 && IsDirectorySeparator(path[volumeSeparatorLength])) i++; + if (path.Length >= volumeSeparatorLength + 1 && IsDirectorySeparator(path[volumeSeparatorLength])) i++; } return i; } - - private static unsafe bool StartsWithOrdinal(char* source, uint sourceLength, string value) - { - if (sourceLength < (uint)value.Length) return false; - for (int i = 0; i < value.Length; i++) - { - if (value[i] != source[i]) return false; - } - return true; - } + #endif /// /// Returns true if the path specified is relative to the current drive or working directory. @@ -287,29 +235,6 @@ namespace System.IO } /// - /// Returns the characters to skip at the start of the path if it starts with space(s) and a drive or directory separator. - /// (examples are " C:", " \") - /// This is a legacy behavior of Path.GetFullPath(). - /// - /// - /// Note that this conflicts with IsPathRooted() which doesn't (and never did) such a skip. - /// - internal static int PathStartSkip(string path) - { - int startIndex = 0; - while (startIndex < path.Length && path[startIndex] == ' ') startIndex++; - - if (startIndex > 0 && (startIndex < path.Length && PathInternal.IsDirectorySeparator(path[startIndex])) - || (startIndex + 1 < path.Length && path[startIndex + 1] == ':' && PathInternal.IsValidDriveChar(path[startIndex]))) - { - // Go ahead and skip spaces as we're either " C:" or " \" - return startIndex; - } - - return 0; - } - - /// /// True if the given character is a directory separator. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -317,105 +242,5 @@ namespace System.IO { return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar; } - - /// - /// Normalize separators in the given path. Converts forward slashes into back slashes and compresses slash runs, keeping initial 2 if present. - /// Also trims initial whitespace in front of "rooted" paths (see PathStartSkip). - /// - /// This effectively replicates the behavior of the legacy NormalizePath when it was called with fullCheck=false and expandShortpaths=false. - /// The current NormalizePath gets directory separator normalization from Win32's GetFullPathName(), which will resolve relative paths and as - /// such can't be used here (and is overkill for our uses). - /// - /// Like the current NormalizePath this will not try and analyze periods/spaces within directory segments. - /// - /// - /// The only callers that used to use Path.Normalize(fullCheck=false) were Path.GetDirectoryName() and Path.GetPathRoot(). Both usages do - /// not need trimming of trailing whitespace here. - /// - /// GetPathRoot() could technically skip normalizing separators after the second segment- consider as a future optimization. - /// - /// For legacy desktop behavior with ExpandShortPaths: - /// - It has no impact on GetPathRoot() so doesn't need consideration. - /// - It could impact GetDirectoryName(), but only if the path isn't relative (C:\ or \\Server\Share). - /// - /// In the case of GetDirectoryName() the ExpandShortPaths behavior was undocumented and provided inconsistent results if the path was - /// fixed/relative. For example: "C:\PROGRA~1\A.TXT" would return "C:\Program Files" while ".\PROGRA~1\A.TXT" would return ".\PROGRA~1". If you - /// ultimately call GetFullPath() this doesn't matter, but if you don't or have any intermediate string handling could easily be tripped up by - /// this undocumented behavior. - /// - /// We won't match this old behavior because: - /// - /// 1. It was undocumented - /// 2. It was costly (extremely so if it actually contained '~') - /// 3. Doesn't play nice with string logic - /// 4. Isn't a cross-plat friendly concept/behavior - /// - internal static string NormalizeDirectorySeparators(string path) - { - if (string.IsNullOrEmpty(path)) return path; - - char current; - int start = PathStartSkip(path); - - if (start == 0) - { - // Make a pass to see if we need to normalize so we can potentially skip allocating - bool normalized = true; - - for (int i = 0; i < path.Length; i++) - { - current = path[i]; - if (IsDirectorySeparator(current) - && (current != Path.DirectorySeparatorChar - // Check for sequential separators past the first position (we need to keep initial two for UNC/extended) - || (i > 0 && i + 1 < path.Length && IsDirectorySeparator(path[i + 1])))) - { - normalized = false; - break; - } - } - - if (normalized) return path; - } - - StringBuilder builder = new StringBuilder(path.Length); - - if (IsDirectorySeparator(path[start])) - { - start++; - builder.Append(Path.DirectorySeparatorChar); - } - - for (int i = start; i < path.Length; i++) - { - current = path[i]; - - // If we have a separator - if (IsDirectorySeparator(current)) - { - // If the next is a separator, skip adding this - if (i + 1 < path.Length && IsDirectorySeparator(path[i + 1])) - { - continue; - } - - // Ensure it is the primary separator - current = Path.DirectorySeparatorChar; - } - - builder.Append(current); - } - - return builder.ToString(); - } - - /// - /// Returns true if the character is a directory or volume separator. - /// - /// The character to test. - internal static bool IsDirectoryOrVolumeSeparator(char ch) - { - return PathInternal.IsDirectorySeparator(ch) || Path.VolumeSeparatorChar == ch; - } } } diff --git a/src/libraries/Common/src/System/IO/PathInternal.cs b/src/libraries/Common/src/System/IO/PathInternal.cs deleted file mode 100644 index c8135cd..0000000 --- a/src/libraries/Common/src/System/IO/PathInternal.cs +++ /dev/null @@ -1,149 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Text; - -namespace System.IO -{ - /// Contains internal path helpers that are shared between many projects. - internal static partial class PathInternal - { - /// - /// Returns true if the given StringBuilder starts with the given value. - /// - /// The string to compare against the start of the StringBuilder. - internal static bool StartsWithOrdinal(this StringBuilder builder, string value) - { - if (value == null || builder.Length < value.Length) - return false; - - for (int i = 0; i < value.Length; i++) - { - if (builder[i] != value[i]) return false; - } - return true; - } - - /// - /// Returns true if the given string starts with the given value. - /// - /// The string to compare against the start of the source string. - internal static bool StartsWithOrdinal(this string source, string value) - { - if (value == null || source.Length < value.Length) - return false; - - return source.StartsWith(value, StringComparison.Ordinal); - } - - /// - /// Trims the specified characters from the end of the StringBuilder. - /// - internal static StringBuilder TrimEnd(this StringBuilder builder, params char[] trimChars) - { - if (trimChars == null || trimChars.Length == 0) - return builder; - - int end = builder.Length - 1; - - for (; end >= 0; end--) - { - int i = 0; - char ch = builder[end]; - for (; i < trimChars.Length; i++) - { - if (trimChars[i] == ch) break; - } - if (i == trimChars.Length) - { - // Not a trim char - break; - } - } - - builder.Length = end + 1; - return builder; - } - - /// - /// Returns true if the path ends in a directory separator. - /// - internal static bool EndsInDirectorySeparator(ReadOnlySpan path) => - path.Length > 0 && IsDirectorySeparator(path[path.Length - 1]); - - /// - /// Get the common path length from the start of the string. - /// - internal static int GetCommonPathLength(string first, string second, bool ignoreCase) - { - int commonChars = EqualStartingCharacterCount(first, second, ignoreCase: ignoreCase); - - // If nothing matches - if (commonChars == 0) - return commonChars; - - // Or we're a full string and equal length or match to a separator - if (commonChars == first.Length - && (commonChars == second.Length || IsDirectorySeparator(second[commonChars]))) - return commonChars; - - if (commonChars == second.Length && IsDirectorySeparator(first[commonChars])) - return commonChars; - - // It's possible we matched somewhere in the middle of a segment e.g. C:\Foodie and C:\Foobar. - while (commonChars > 0 && !IsDirectorySeparator(first[commonChars - 1])) - commonChars--; - - return commonChars; - } - - /// - /// Gets the count of common characters from the left optionally ignoring case - /// - internal static unsafe int EqualStartingCharacterCount(string first, string second, bool ignoreCase) - { - if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) return 0; - - int commonChars = 0; - - fixed (char* f = first) - fixed (char* s = second) - { - char* l = f; - char* r = s; - char* leftEnd = l + first.Length; - char* rightEnd = r + second.Length; - - while (l != leftEnd && r != rightEnd - && (*l == *r || (ignoreCase && char.ToUpperInvariant((*l)) == char.ToUpperInvariant((*r))))) - { - commonChars++; - l++; - r++; - } - } - - return commonChars; - } - - /// - /// Returns true if the two paths have the same root - /// - internal static bool AreRootsEqual(string first, string second, StringComparison comparisonType) - { - int firstRootLength = GetRootLength(first); - int secondRootLength = GetRootLength(second); - - return firstRootLength == secondRootLength - && string.Compare( - strA: first, - indexA: 0, - strB: second, - indexB: 0, - length: firstRootLength, - comparisonType: comparisonType) == 0; - } - } -} diff --git a/src/libraries/Common/tests/Common.Tests.csproj b/src/libraries/Common/tests/Common.Tests.csproj index 76fa174..ad647fd 100644 --- a/src/libraries/Common/tests/Common.Tests.csproj +++ b/src/libraries/Common/tests/Common.Tests.csproj @@ -38,9 +38,6 @@ Common\System\Collections\Generic\LargeArrayBuilder.cs - - Common\System\IO\PathInternal.cs - Common\System\IO\PathInternal.CaseSensitivity.cs @@ -82,9 +79,7 @@ - - System\Net\Sockets\Fletcher32.cs @@ -142,8 +137,9 @@ + - + \ No newline at end of file diff --git a/src/libraries/Common/tests/Tests/System/IO/PathInternal.Tests.cs b/src/libraries/Common/tests/Tests/System/IO/PathInternal.Tests.cs deleted file mode 100644 index 4435fb8..0000000 --- a/src/libraries/Common/tests/Tests/System/IO/PathInternal.Tests.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.Text; -using Xunit; - -namespace Tests.System.IO -{ - public class PathInternalTests - { - [Theory, - InlineData("Foo", "Foos"), - InlineData("Foo", null)] - public void StartsWithOrdinal_NegativeCases(string source, string value) - { - Assert.False(PathInternal.StartsWithOrdinal(source, value)); - Assert.False(PathInternal.StartsWithOrdinal(new StringBuilder(source), value)); - } - - [Theory, - InlineData("FOO", "foo", false), - InlineData("FOO", "FOO", true), - InlineData("FOO", "fo", false), - InlineData("FOO", "FO", true), - InlineData("FOO", "oo", false), - InlineData("FOO", "OO", false)] - public void StartsWithOrdinal_PositiveCases(string source, string value, bool expected) - { - Assert.Equal(expected, PathInternal.StartsWithOrdinal(source, value)); - Assert.Equal(expected, PathInternal.StartsWithOrdinal(new StringBuilder(source), value)); - } - - [Theory, - InlineData("", "", true, 0), - InlineData("", "", false, 0), - InlineData("a", "", true, 0), - InlineData("a", "", false, 0), - InlineData("", "b", true, 0), - InlineData("", "b", false, 0), - InlineData("\0", "\0", true, 1), - InlineData("\0", "\0", false, 1), - InlineData("ABcd", "ABCD", true, 4), - InlineData("ABCD", "ABcd", true, 4), - InlineData("ABcd", "ABCD", false, 2), - InlineData("ABCD", "ABcd", false, 2), - InlineData("AB\0cd", "AB\0CD", true, 5), - InlineData("AB\0CD", "AB\0cd", true, 5), - InlineData("AB\0cd", "AB\0CD", false, 3), - InlineData("AB\0CD", "AB\0cd", false, 3), - InlineData("ABc\0", "ABC\0", true, 4), - InlineData("ABC\0", "ABc\0", true, 4), - InlineData("ABc\0", "ABC\0", false, 2), - InlineData("ABC\0", "ABc\0", false, 2), - InlineData("ABcdxyzl", "ABCDpdq", true, 4), - InlineData("ABCDxyz", "ABcdpdql", true, 4), - InlineData("ABcdxyz", "ABCDpdq", false, 2), - InlineData("ABCDxyzoo", "ABcdpdq", false, 2)] - public void EqualStartingCharacterCount(string first, string second, bool ignoreCase, int expected) - { - Assert.Equal(expected, PathInternal.EqualStartingCharacterCount(first, second, ignoreCase)); - } - - - [Theory, - InlineData(@"", @"", true, 0), - InlineData(@"", @"", false, 0), - InlineData(@"a", @"A", true, 1), - InlineData(@"A", @"a", true, 1), - InlineData(@"a", @"A", false, 0), - InlineData(@"A", @"a", false, 0), - InlineData(@"foo", @"foobar", true, 0), - InlineData(@"foo", @"foobar", false, 0), - InlineData(@"foo", @"foo/bar", true, 3), - InlineData(@"foo", @"foo/bar", false, 3), - InlineData(@"foo/", @"foo/bar", true, 4), - InlineData(@"foo/", @"foo/bar", false, 4), - InlineData(@"foo/bar", @"foo/bar", true, 7), - InlineData(@"foo/bar", @"foo/bar", false, 7), - InlineData(@"foo/bar", @"foo/BAR", true, 7), - InlineData(@"foo/bar", @"foo/BAR", false, 4), - InlineData(@"foo/bar", @"foo/barb", true, 4), - InlineData(@"foo/bar", @"foo/barb", false, 4)] - public void GetCommonPathLength(string first, string second, bool ignoreCase, int expected) - { - Assert.Equal(expected, PathInternal.GetCommonPathLength(first, second, ignoreCase)); - } - } -} diff --git a/src/libraries/Common/tests/Tests/System/IO/PathInternal.Windows.Tests.cs b/src/libraries/Common/tests/Tests/System/IO/PathInternal.Windows.Tests.cs index 799dfc8..9d48b37 100644 --- a/src/libraries/Common/tests/Tests/System/IO/PathInternal.Windows.Tests.cs +++ b/src/libraries/Common/tests/Tests/System/IO/PathInternal.Windows.Tests.cs @@ -4,29 +4,12 @@ using System; using System.IO; -using System.Runtime.InteropServices; using Xunit; public class PathInternal_Windows_Tests { [Theory, - InlineData(@"\\?\", false), - InlineData(@"\\?\?", true), - InlineData(@"//?/", false), - InlineData(@"//?/*", true), - InlineData(@"\\.\>", true), - InlineData(@"C:\", false), - InlineData(@"C:\<", true), - InlineData("\"MyFile\"", true) - ] - [PlatformSpecific(TestPlatforms.Windows)] - public void HasWildcardCharacters(string path, bool expected) - { - Assert.Equal(expected, PathInternal.HasWildCardCharacters(path)); - } - - [Theory, - InlineData(PathInternal.ExtendedPathPrefix, PathInternal.ExtendedPathPrefix), + InlineData(PathInternal.ExtendedDevicePathPrefix, PathInternal.ExtendedDevicePathPrefix), InlineData(@"Foo", @"Foo"), InlineData(@"C:\Foo", @"\\?\C:\Foo"), InlineData(@"\\.\Foo", @"\\.\Foo"), @@ -100,83 +83,4 @@ public class PathInternal_Windows_Tests { Assert.Equal(expected, PathInternal.IsPartiallyQualified(path)); } - - [Theory, - InlineData(@"", 0), - InlineData(@" :", 0), - InlineData(@" C:", 2), - InlineData(@" C:\", 3), - InlineData(@"C:\", 0), - InlineData(@" ", 0), - InlineData(@" \", 2), - InlineData(@" 8:", 0), - InlineData(@" \\", 4), - InlineData(@"\\", 0) - ] - [PlatformSpecific(TestPlatforms.Windows)] - public void PathStartSkipTest(string path, int expected) - { - Assert.Equal(expected, PathInternal.PathStartSkip(path)); - } - - [Theory, - InlineData(@"", @""), - InlineData(null, null), - InlineData(@"\", @"\"), - InlineData(@"/", @"\"), - InlineData(@"\\", @"\\"), - InlineData(@"\\\", @"\\"), - InlineData(@"//", @"\\"), - InlineData(@"///", @"\\"), - InlineData(@"\/", @"\\"), - InlineData(@"\/\", @"\\"), - - InlineData(@"a\a", @"a\a"), - InlineData(@"a\\a", @"a\a"), - InlineData(@"a/a", @"a\a"), - InlineData(@"a//a", @"a\a"), - InlineData(@"a\", @"a\"), - InlineData(@"a\\", @"a\"), - InlineData(@"a/", @"a\"), - InlineData(@"a//", @"a\"), - InlineData(@"\a", @"\a"), - InlineData(@"\\a", @"\\a"), - InlineData(@"/a", @"\a"), - InlineData(@"//a", @"\\a"), - - // Skip tests - InlineData(@" :", @" :"), - InlineData(@" C:", @"C:"), - InlineData(@" C:\", @"C:\"), - InlineData(@" C:/", @"C:\"), - InlineData(@" ", @" "), - InlineData(@" \", @"\"), - InlineData(@" /", @"\"), - InlineData(@" 8:", @" 8:"), - InlineData(@" \\", @"\\"), - InlineData(@" //", @"\\") - ] - [PlatformSpecific(TestPlatforms.Windows)] - public void NormalizeDirectorySeparatorTests(string path, string expected) - { - string result = PathInternal.NormalizeDirectorySeparators(path); - Assert.Equal(expected, result); - if (string.Equals(path, expected, StringComparison.Ordinal)) - Assert.Same(path, result); - } - - [Theory, - InlineData(@"", @"", StringComparison.OrdinalIgnoreCase, true), - InlineData(@"", @"", StringComparison.Ordinal, true), - InlineData(@"A", @"a", StringComparison.OrdinalIgnoreCase, true), - InlineData(@"A", @"a", StringComparison.Ordinal, true), - InlineData(@"C:\", @"c:\", StringComparison.OrdinalIgnoreCase, true), - InlineData(@"C:\", @"c:\", StringComparison.Ordinal, false) - ] - [PlatformSpecific(TestPlatforms.Windows)] - public void AreRootsEqual(string first, string second, StringComparison comparisonType, bool expected) - { - Assert.Equal(expected, PathInternal.AreRootsEqual(first, second, comparisonType)); - } - } diff --git a/src/libraries/Common/tests/Tests/System/IO/PathInternal_Unix_Tests.cs b/src/libraries/Common/tests/Tests/System/IO/PathInternal_Unix_Tests.cs deleted file mode 100644 index 5eac916..0000000 --- a/src/libraries/Common/tests/Tests/System/IO/PathInternal_Unix_Tests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.Text; -using Xunit; - -public class PathInternal_Unix_Tests -{ - [Theory, - InlineData(@"", @""), - InlineData(null, null), - InlineData(@"/", @"/"), - InlineData(@"//", @"/"), - InlineData(@"///", @"/"), - InlineData(@"\", @"\"), - InlineData(@"\\", @"\\"), - InlineData(@"\\\", @"\\\"), - InlineData(@"\/", @"\/"), - InlineData(@"\/\", @"\/\"), - - InlineData(@"a/a", @"a/a"), - InlineData(@"a//a", @"a/a"), - InlineData(@"a\\a", @"a\\a"), - InlineData(@"/a", @"/a"), - InlineData(@"//a", @"/a"), - InlineData(@"\\a", @"\\a"), - InlineData(@"a/", @"a/"), - InlineData(@"a//", @"a/"), - InlineData(@"a\\", @"a\\"), - ] - [PlatformSpecific(TestPlatforms.AnyUnix)] - public void NormalizeDirectorySeparatorTests(string path, string expected) - { - string result = PathInternal.NormalizeDirectorySeparators(path); - Assert.Equal(expected, result); - if (string.Equals(path, expected, StringComparison.Ordinal)) - Assert.Same(path, result); - } -} diff --git a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj index 9cacdec..0353808 100644 --- a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj +++ b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj @@ -58,9 +58,6 @@ - - Common\System\IO\PathInternal.cs - Common\System\IO\StreamHelpers.CopyValidation.cs diff --git a/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj b/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj index 056e32b..85f67d9 100644 --- a/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj +++ b/src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj @@ -20,9 +20,6 @@ Common\System\HResults.cs - - Common\System\IO\PathInternal.cs - Common\System\IO\PathInternal.CaseSensitivity.cs @@ -101,6 +98,7 @@ + diff --git a/src/libraries/System.IO.FileSystem.Watcher/src/System.IO.FileSystem.Watcher.csproj b/src/libraries/System.IO.FileSystem.Watcher/src/System.IO.FileSystem.Watcher.csproj index 734563e..566798b 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/src/System.IO.FileSystem.Watcher.csproj +++ b/src/libraries/System.IO.FileSystem.Watcher/src/System.IO.FileSystem.Watcher.csproj @@ -29,9 +29,6 @@ - - Common\System\IO\PathInternal.cs - Common\System\IO\PathInternal.CaseSensitivity.cs @@ -150,6 +147,7 @@ + diff --git a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln index 7da304a..5d90f6e 100644 --- a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln +++ b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln @@ -39,8 +39,8 @@ Global {3C42F714-82AF-4A43-9B9C-744DE31B5C5D}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {3C42F714-82AF-4A43-9B9C-744DE31B5C5D}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU {3C42F714-82AF-4A43-9B9C-744DE31B5C5D}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU - {1B528B61-14F9-4BFC-A79A-F0BDB3339150}.Debug|Any CPU.ActiveCfg = netcoreapp-Unix-Debug|Any CPU - {1B528B61-14F9-4BFC-A79A-F0BDB3339150}.Debug|Any CPU.Build.0 = netcoreapp-Unix-Debug|Any CPU + {1B528B61-14F9-4BFC-A79A-F0BDB3339150}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {1B528B61-14F9-4BFC-A79A-F0BDB3339150}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {1B528B61-14F9-4BFC-A79A-F0BDB3339150}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU {1B528B61-14F9-4BFC-A79A-F0BDB3339150}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {4B15C12E-B6AB-4B05-8ECA-C2E2AEA67482}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU diff --git a/src/libraries/System.IO.FileSystem/src/System.IO.FileSystem.csproj b/src/libraries/System.IO.FileSystem/src/System.IO.FileSystem.csproj index 93d318a..f6ddc7b 100644 --- a/src/libraries/System.IO.FileSystem/src/System.IO.FileSystem.csproj +++ b/src/libraries/System.IO.FileSystem/src/System.IO.FileSystem.csproj @@ -44,9 +44,6 @@ Common\CoreLib\System\Text\ValueStringBuilder.cs - - Common\System\IO\PathInternal.cs - Common\System\IO\PathInternal.CaseSensitivity.cs @@ -62,10 +59,7 @@ - - - @@ -285,10 +279,7 @@ - - - Common\Interop\Unix\Interop.Libraries.cs diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.Unix.cs b/src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.Unix.cs deleted file mode 100644 index 307df6c..0000000 --- a/src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.Unix.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.IO -{ - partial class DirectoryInfo - { - internal static unsafe DirectoryInfo Create(string fullPath, string fileName, ref FileStatus fileStatus) - { - DirectoryInfo info = new DirectoryInfo(fullPath, fileName: fileName, isNormalized: true); - info.Init(ref fileStatus); - return info; - } - } -} diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.Windows.cs b/src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.Windows.cs deleted file mode 100644 index 84e26c6..0000000 --- a/src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.Windows.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.IO.Enumeration; - -namespace System.IO -{ - partial class DirectoryInfo - { - internal static unsafe DirectoryInfo Create(string fullPath, ref FileSystemEntry findData) - { - DirectoryInfo info = new DirectoryInfo(fullPath, fileName: findData.FileName.GetStringFromFixedBuffer(), isNormalized: true); - info.Init(findData._info); - return info; - } - } -} diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.cs b/src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.cs index d4ba432..be18612 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.cs @@ -29,7 +29,7 @@ namespace System.IO OriginalPath = originalPath ?? throw new ArgumentNullException("path"); fullPath = fullPath ?? originalPath; - Debug.Assert(!isNormalized || !PathInternal.IsPartiallyQualified(fullPath), "should be fully qualified if normalized"); + Debug.Assert(!isNormalized || !PathInternal.IsPartiallyQualified(fullPath), $"'{fullPath}' should be fully qualified if normalized"); fullPath = isNormalized ? fullPath : Path.GetFullPath(fullPath); _name = fileName ?? (PathHelpers.IsRoot(fullPath) ? diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Unix.cs b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Unix.cs index 148364f..0d4d210 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Unix.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Unix.cs @@ -17,7 +17,7 @@ namespace System.IO.Enumeration private ReadOnlySpan _fileName; private fixed char _fileNameBuffer[FileNameBufferSize]; - internal static bool Initialize( + internal static FileAttributes Initialize( ref FileSystemEntry entry, Interop.Sys.DirectoryEntry directoryEntry, ReadOnlySpan directory, @@ -33,9 +33,7 @@ namespace System.IO.Enumeration entry._fullPath = ReadOnlySpan.Empty; entry._fileName = ReadOnlySpan.Empty; - // Get from the dir entry whether the entry is a file or directory. - // We classify everything as a file unless we know it to be a directory. - // (This includes regular files, FIFOs, etc.) + // IMPORTANT: Attribute logic must match the logic in FileStatus bool isDirectory = false; if (directoryEntry.InodeType == Interop.Sys.NodeType.DT_DIR) @@ -43,16 +41,25 @@ namespace System.IO.Enumeration // We know it's a directory. isDirectory = true; } - else if ((directoryEntry.InodeType == Interop.Sys.NodeType.DT_LNK || directoryEntry.InodeType == Interop.Sys.NodeType.DT_UNKNOWN) + else if ((directoryEntry.InodeType == Interop.Sys.NodeType.DT_LNK) && Interop.Sys.Stat(entry.FullPath, out Interop.Sys.FileStatus targetStatus) >= 0) { - // It's a symlink or unknown: stat to it to see if we can resolve it to a directory. + // It's a symlink: stat to it to see if we can resolve it to a directory. isDirectory = (targetStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR; } entry._status = default; FileStatus.Initialize(ref entry._status, isDirectory); - return isDirectory; + + FileAttributes attributes = FileAttributes.Normal; + if (directoryEntry.InodeType == Interop.Sys.NodeType.DT_LNK) + attributes |= FileAttributes.ReparsePoint; + if (isDirectory) + attributes |= FileAttributes.Directory; + if (directoryEntry.Name[0] == '.') + attributes |= FileAttributes.Hidden; + + return attributes; } @@ -116,14 +123,7 @@ namespace System.IO.Enumeration public FileSystemInfo ToFileSystemInfo() { string fullPath = ToFullPath(); - if (_status.InitiallyDirectory) - { - return DirectoryInfo.Create(fullPath, new string(FileName), ref _status); - } - else - { - return FileInfo.Create(fullPath, new string(FileName), ref _status); - } + return FileSystemInfo.Create(fullPath, new string(FileName), ref _status); } /// diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Windows.cs b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Windows.cs index 48bf820..99292c5 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Windows.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Windows.cs @@ -68,13 +68,7 @@ namespace System.IO.Enumeration public bool IsDirectory => (Attributes & FileAttributes.Directory) != 0; public FileSystemInfo ToFileSystemInfo() - { - string fullPath = PathHelpers.CombineNoChecks(Directory, FileName); - - return IsDirectory - ? DirectoryInfo.Create(fullPath, ref this) - : (FileSystemInfo)FileInfo.Create(fullPath, ref this); - } + => FileSystemInfo.Create(PathHelpers.CombineNoChecks(Directory, FileName), ref this); /// /// Returns the full path for find results, based on the initially provided path. diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs index ae1c5fb..4a0ccb8 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs @@ -107,7 +107,8 @@ namespace System.IO.Enumeration if (_lastEntryFound) return false; - bool isDirectory = FileSystemEntry.Initialize(ref entry, _entry, _currentPath, _rootDirectory, _originalRootDirectory, new Span(_pathBuffer)); + FileAttributes attributes = FileSystemEntry.Initialize(ref entry, _entry, _currentPath, _rootDirectory, _originalRootDirectory, new Span(_pathBuffer)); + bool isDirectory = (attributes & FileAttributes.Directory) != 0; bool isSpecialDirectory = false; if (isDirectory) @@ -124,17 +125,14 @@ namespace System.IO.Enumeration if (!isSpecialDirectory && _options.AttributesToSkip != 0) { - if ((_options.AttributesToSkip & ~(FileAttributes.Directory | FileAttributes.Hidden | FileAttributes.ReparsePoint)) == 0) + if ((_options.AttributesToSkip & FileAttributes.ReadOnly) != 0) { - // These three we don't have to hit the disk again to evaluate - if (((_options.AttributesToSkip & FileAttributes.Directory) != 0 && isDirectory) - || ((_options.AttributesToSkip & FileAttributes.Hidden) != 0 && _entry.Name[0] == '.') - || ((_options.AttributesToSkip & FileAttributes.ReparsePoint) != 0 && _entry.InodeType == Interop.Sys.NodeType.DT_LNK)) - continue; + // ReadOnly is the only attribute that requires hitting entry.Attributes (which hits the disk) + attributes = entry.Attributes; } - else if (entry.Attributes != (FileAttributes)(-1) && (_options.AttributesToSkip & entry.Attributes) != 0) + + if (attributes != (FileAttributes)(-1) && (_options.AttributesToSkip & attributes) != 0) { - // Hitting Attributes on the FileSystemEntry will cause a stat call continue; } } diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/FileInfo.Unix.cs b/src/libraries/System.IO.FileSystem/src/System/IO/FileInfo.Unix.cs deleted file mode 100644 index e88bb9a..0000000 --- a/src/libraries/System.IO.FileSystem/src/System/IO/FileInfo.Unix.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.IO -{ - partial class FileInfo - { - internal static unsafe FileInfo Create(string fullPath, string fileName, ref FileStatus fileStatus) - { - FileInfo info = new FileInfo(fullPath, fileName: fileName, isNormalized: true); - info.Init(ref fileStatus); - return info; - } - } -} diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/FileInfo.Windows.cs b/src/libraries/System.IO.FileSystem/src/System/IO/FileInfo.Windows.cs deleted file mode 100644 index 8820620..0000000 --- a/src/libraries/System.IO.FileSystem/src/System/IO/FileInfo.Windows.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.IO.Enumeration; - -namespace System.IO -{ - partial class FileInfo - { - internal static unsafe FileInfo Create(string fullPath, ref FileSystemEntry findData) - { - FileInfo info = new FileInfo(fullPath, fileName: findData.FileName.GetStringFromFixedBuffer(), isNormalized: true); - info.Init(findData._info); - return info; - } - } -} diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs b/src/libraries/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs index c9a3ae5..387cc32 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs @@ -37,6 +37,8 @@ namespace System.IO public FileAttributes GetAttributes(ReadOnlySpan path, ReadOnlySpan fileName) { + // IMPORTANT: Attribute logic must match the logic in FileSystemEntry + EnsureStatInitialized(path); if (!_exists) @@ -44,51 +46,41 @@ namespace System.IO FileAttributes attrs = default; - bool IsReadOnly(ref Interop.Sys.FileStatus fileStatus) + Interop.Sys.Permissions readBit, writeBit; + if (_fileStatus.Uid == Interop.Sys.GetEUid()) { - Interop.Sys.Permissions readBit, writeBit; - if (fileStatus.Uid == Interop.Sys.GetEUid()) - { - // User effectively owns the file - readBit = Interop.Sys.Permissions.S_IRUSR; - writeBit = Interop.Sys.Permissions.S_IWUSR; - } - else if (fileStatus.Gid == Interop.Sys.GetEGid()) - { - // User belongs to a group that effectively owns the file - readBit = Interop.Sys.Permissions.S_IRGRP; - writeBit = Interop.Sys.Permissions.S_IWGRP; - } - else - { - // Others permissions - readBit = Interop.Sys.Permissions.S_IROTH; - writeBit = Interop.Sys.Permissions.S_IWOTH; - } - - return - (fileStatus.Mode & (int)readBit) != 0 && // has read permission - (fileStatus.Mode & (int)writeBit) == 0; // but not write permission + // User effectively owns the file + readBit = Interop.Sys.Permissions.S_IRUSR; + writeBit = Interop.Sys.Permissions.S_IWUSR; } - - if (_isDirectory) // this is the one attribute where we follow symlinks + else if (_fileStatus.Gid == Interop.Sys.GetEGid()) { - attrs |= FileAttributes.Directory; + // User belongs to a group that effectively owns the file + readBit = Interop.Sys.Permissions.S_IRGRP; + writeBit = Interop.Sys.Permissions.S_IWGRP; } - if (IsReadOnly(ref _fileStatus)) + else + { + // Others permissions + readBit = Interop.Sys.Permissions.S_IROTH; + writeBit = Interop.Sys.Permissions.S_IWOTH; + } + + if ((_fileStatus.Mode & (int)readBit) != 0 && // has read permission + (_fileStatus.Mode & (int)writeBit) == 0) // but not write permission { attrs |= FileAttributes.ReadOnly; } + if ((_fileStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFLNK) - { attrs |= FileAttributes.ReparsePoint; - } + + if (_isDirectory) + attrs |= FileAttributes.Directory; // If the filename starts with a period, it's hidden. if (fileName.Length > 0 && fileName[0] == '.') - { attrs |= FileAttributes.Hidden; - } return attrs != default ? attrs : FileAttributes.Normal; } @@ -227,7 +219,7 @@ namespace System.IO // storing those results separately. We only report failure if the initial // lstat fails, as a broken symlink should still report info on exists, attributes, etc. _isDirectory = false; - if (path.Length > 1 && PathInternal.EndsInDirectorySeparator(path)) + if (path.Length > 1 && PathHelpers.EndsInDirectorySeparator(path)) path = path.Slice(0, path.Length - 1); int result = Interop.Sys.LStat(path, out _fileStatus); if (result < 0) @@ -250,6 +242,8 @@ namespace System.IO } _exists = true; + + // IMPORTANT: Is directory logic must match the logic in FileSystemEntry _isDirectory = (_fileStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR; // If we're a symlink, attempt to check the target to see if it is a directory diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs b/src/libraries/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs index 1416544..c96aea1 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs @@ -13,6 +13,16 @@ namespace System.IO FileStatus.Initialize(ref _fileStatus, this is DirectoryInfo); } + internal static unsafe FileSystemInfo Create(string fullPath, string fileName, ref FileStatus fileStatus) + { + FileSystemInfo info = fileStatus.InitiallyDirectory + ? (FileSystemInfo)new DirectoryInfo(fullPath, fileName: fileName, isNormalized: true) + : new FileInfo(fullPath, fileName: fileName, isNormalized: true); + + info.Init(ref fileStatus); + return info; + } + internal void Invalidate() => _fileStatus.Invalidate(); internal unsafe void Init(ref FileStatus fileStatus) diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs b/src/libraries/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs index f45a713..e369567 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Security; +using System.IO.Enumeration; namespace System.IO { @@ -21,6 +21,16 @@ namespace System.IO { } + internal static unsafe FileSystemInfo Create(string fullPath, ref FileSystemEntry findData) + { + FileSystemInfo info = findData.IsDirectory + ? (FileSystemInfo) new DirectoryInfo(fullPath, fileName: new string(findData.FileName), isNormalized: true) + : new FileInfo(fullPath, fileName: new string(findData.FileName), isNormalized: true); + + info.Init(findData._info); + return info; + } + internal void Invalidate() { _dataInitialized = -1; diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.Unix.cs b/src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.Unix.cs deleted file mode 100644 index 47dcd9b..0000000 --- a/src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.Unix.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.IO -{ - internal static partial class PathHelpers - { - internal static bool ShouldReviseDirectoryPathToCurrent(string path) - { - // Unlike on Windows, there are no special cases on Unix where we'd want to ignore - // user-provided path and instead automatically use the current directory. - return false; - } - - internal static string TrimEndingDirectorySeparator(string path) => - path.Length > 1 && PathInternal.IsDirectorySeparator(path[path.Length - 1]) ? // exclude root "/" - path.Substring(0, path.Length - 1) : - path; - } -} diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.Windows.cs b/src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.Windows.cs deleted file mode 100644 index 3c30eaf..0000000 --- a/src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.Windows.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.InteropServices; - -namespace System.IO -{ - internal static partial class PathInternal - { - internal static unsafe int GetRootLength(ReadOnlySpan path) - { - fixed (char* p = &MemoryMarshal.GetReference(path)) - { - return (int)GetRootLength(p, (uint)path.Length); - } - } - } - - internal static partial class PathHelpers - { - internal static string TrimEndingDirectorySeparator(string path) => - EndsInDirectorySeparator(path) ? - path.Substring(0, path.Length - 1) : - path; - } -} diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.cs b/src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.cs index 5e6a884..cb2ce24 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.cs @@ -33,15 +33,21 @@ namespace System.IO throw new ArgumentException(SR.Arg_Path2IsRooted, nameof(path2)); } - internal static bool IsRoot(string path) - { - return path.Length == PathInternal.GetRootLength(path); - } + internal static bool IsRoot(ReadOnlySpan path) + => path.Length == PathInternal.GetRootLength(path); - internal static bool EndsInDirectorySeparator(string path) - { - return path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]); - } + internal static bool EndsInDirectorySeparator(ReadOnlySpan path) + => path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]); + + internal static string TrimEndingDirectorySeparator(string path) => + EndsInDirectorySeparator(path) && !IsRoot(path) ? + path.Substring(0, path.Length - 1) : + path; + + internal static ReadOnlySpan TrimEndingDirectorySeparator(ReadOnlySpan path) => + EndsInDirectorySeparator(path) && !IsRoot(path) ? + path.Slice(0, path.Length - 1) : + path; /// /// Combines two paths. Does no validation of paths, only concatenates the paths diff --git a/src/libraries/System.IO.Ports/src/System.IO.Ports.csproj b/src/libraries/System.IO.Ports/src/System.IO.Ports.csproj index c5f4f02..1a710ac 100644 --- a/src/libraries/System.IO.Ports/src/System.IO.Ports.csproj +++ b/src/libraries/System.IO.Ports/src/System.IO.Ports.csproj @@ -6,6 +6,7 @@ {187503F4-BEF9-4369-A1B2-E3DC5D564E4E} true SR.PlatformNotSupported_IOPorts + $(DefineConstants);NOSPAN true diff --git a/src/libraries/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj b/src/libraries/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj index 3178bc3..718d19f 100644 --- a/src/libraries/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj +++ b/src/libraries/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj @@ -79,9 +79,6 @@ Common\System\IO\StringBuilderCache.cs - - Common\System\IO\PathInternal.cs - Common\System\HResults.cs @@ -172,9 +169,6 @@ Common\System\IO\DriveInfoInternal.Win32.cs - - Common\System\IO\PathInternal.Windows.cs - @@ -241,9 +235,6 @@ Common\System\IO\DriveInfoInternal.Unix.cs - - Common\System\IO\PathInternal.Unix.cs - Common\System\IO\PersistedFiles.Unix.cs @@ -271,4 +262,4 @@ - + \ No newline at end of file diff --git a/src/libraries/System.Runtime.Extensions/src/System/Environment.Windows.cs b/src/libraries/System.Runtime.Extensions/src/System/Environment.Windows.cs index 2caff31..a835b07 100644 --- a/src/libraries/System.Runtime.Extensions/src/System/Environment.Windows.cs +++ b/src/libraries/System.Runtime.Extensions/src/System/Environment.Windows.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; diff --git a/src/libraries/System.Runtime.Extensions/src/System/Environment.cs b/src/libraries/System.Runtime.Extensions/src/System/Environment.cs index 833ed29..448b434 100644 --- a/src/libraries/System.Runtime.Extensions/src/System/Environment.cs +++ b/src/libraries/System.Runtime.Extensions/src/System/Environment.cs @@ -2,13 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Internal.Runtime.Augments; using System.Collections; using System.Collections.Generic; -using System.IO; using System.Reflection; using System.Runtime.CompilerServices; -using System.Text; +using Internal.Runtime.Augments; namespace System {