Cleaning out dead code (dotnet/corefx#27150)
authorJeremy Kuhne <jeremy.kuhne@microsoft.com>
Thu, 15 Feb 2018 06:49:17 +0000 (22:49 -0800)
committerGitHub <noreply@github.com>
Thu, 15 Feb 2018 06:49:17 +0000 (22:49 -0800)
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

31 files changed:
src/libraries/Common/src/Interop/Windows/kernel32/Interop.GetLongPathName.cs
src/libraries/Common/src/System/IO/PathInternal.Unix.cs
src/libraries/Common/src/System/IO/PathInternal.Windows.cs
src/libraries/Common/src/System/IO/PathInternal.cs [deleted file]
src/libraries/Common/tests/Common.Tests.csproj
src/libraries/Common/tests/Tests/System/IO/PathInternal.Tests.cs [deleted file]
src/libraries/Common/tests/Tests/System/IO/PathInternal.Windows.Tests.cs
src/libraries/Common/tests/Tests/System/IO/PathInternal_Unix_Tests.cs [deleted file]
src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
src/libraries/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj
src/libraries/System.IO.FileSystem.Watcher/src/System.IO.FileSystem.Watcher.csproj
src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln
src/libraries/System.IO.FileSystem/src/System.IO.FileSystem.csproj
src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.Unix.cs [deleted file]
src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.Windows.cs [deleted file]
src/libraries/System.IO.FileSystem/src/System/IO/DirectoryInfo.cs
src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Unix.cs
src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Windows.cs
src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs
src/libraries/System.IO.FileSystem/src/System/IO/FileInfo.Unix.cs [deleted file]
src/libraries/System.IO.FileSystem/src/System/IO/FileInfo.Windows.cs [deleted file]
src/libraries/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs
src/libraries/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs
src/libraries/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs
src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.Unix.cs [deleted file]
src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.Windows.cs [deleted file]
src/libraries/System.IO.FileSystem/src/System/IO/PathHelpers.cs
src/libraries/System.IO.Ports/src/System.IO.Ports.csproj
src/libraries/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj
src/libraries/System.Runtime.Extensions/src/System/Environment.Windows.cs
src/libraries/System.Runtime.Extensions/src/System/Environment.cs

index 185f0c7..1030a57 100644 (file)
@@ -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
     {
         /// <summary>
-        /// WARNING: This method does not implicitly handle long paths. Use GetLongPathName.
+        /// WARNING: This method does not implicitly handle long paths.
         /// </summary>
         [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);
     }
 }
index 27424cb..8e27f4b 100644 (file)
@@ -11,12 +11,6 @@ namespace System.IO
     /// <summary>Contains internal path helpers that are shared between many projects.</summary>
     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<char> path)
         {
             return path.Length > 0 && IsDirectorySeparator(path[0]) ? 1 : 0;
@@ -30,57 +24,6 @@ namespace System.IO
             return c == Path.DirectorySeparatorChar;
         }
 
-        /// <summary>
-        /// Normalize separators in the given path. Compresses forward slash runs.
-        /// </summary>
-        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();
-        }
-
-        /// <summary>
-        /// Returns true if the character is a directory or volume separator.
-        /// </summary>
-        /// <param name="ch">The character to test.</param>
-        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)
         {
index 7307969..6bc6a0f 100644 (file)
@@ -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 =
-        {
-            '\"', '<', '>', '*', '?'
-        };
 
         /// <summary>
         /// 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;
         }
 
         /// <summary>
@@ -168,85 +144,57 @@ namespace System.IO
                 && path[3] == '\\';
         }
 
-        /// <summary>
-        /// Check for known wildcard characters. '*' and '?' are the most common ones.
-        /// </summary>
-        [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
         /// <summary>
         /// Gets the length of the root of the path (drive, share, etc.).
         /// </summary>
-        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<char> 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
 
         /// <summary>
         /// Returns true if the path specified is relative to the current drive or working directory.
@@ -287,29 +235,6 @@ namespace System.IO
         }
 
         /// <summary>
-        /// 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().
-        /// </summary>
-        /// <remarks>
-        /// Note that this conflicts with IsPathRooted() which doesn't (and never did) such a skip.
-        /// </remarks>
-        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;
-        }
-
-        /// <summary>
         /// True if the given character is a directory separator.
         /// </summary>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -317,105 +242,5 @@ namespace System.IO
         {
             return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar;
         }
-
-        /// <summary>
-        /// 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.
-        /// </summary>
-        /// <remarks>
-        /// 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
-        /// </remarks>
-        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();
-        }
-        
-        /// <summary>
-        /// Returns true if the character is a directory or volume separator.
-        /// </summary>
-        /// <param name="ch">The character to test.</param>
-        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 (file)
index c8135cd..0000000
+++ /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
-{
-    /// <summary>Contains internal path helpers that are shared between many projects.</summary>
-    internal static partial class PathInternal
-    {
-        /// <summary>
-        /// Returns true if the given StringBuilder starts with the given value.
-        /// </summary>
-        /// <param name="value">The string to compare against the start of the StringBuilder.</param>
-        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;
-        }
-
-        /// <summary>
-        /// Returns true if the given string starts with the given value.
-        /// </summary>
-        /// <param name="value">The string to compare against the start of the source string.</param>
-        internal static bool StartsWithOrdinal(this string source, string value)
-        {
-            if (value == null || source.Length < value.Length)
-                return false;
-
-            return source.StartsWith(value, StringComparison.Ordinal);
-        }
-
-        /// <summary>
-        /// Trims the specified characters from the end of the StringBuilder.
-        /// </summary>
-        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;
-        }
-
-        /// <summary>
-        /// Returns true if the path ends in a directory separator.
-        /// </summary>
-        internal static bool EndsInDirectorySeparator(ReadOnlySpan<char> path) =>
-            path.Length > 0 && IsDirectorySeparator(path[path.Length - 1]);
-
-        /// <summary>
-        /// Get the common path length from the start of the string.
-        /// </summary>
-        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;
-        }
-
-        /// <summary>
-        /// Gets the count of common characters from the left optionally ignoring case
-        /// </summary>
-        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;
-        }
-
-        /// <summary>
-        /// Returns true if the two paths have the same root
-        /// </summary>
-        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;
-        }
-    }
-}
index 76fa174..ad647fd 100644 (file)
@@ -38,9 +38,6 @@
     <Compile Include="$(CommonPath)\System\Collections\Generic\LargeArrayBuilder.cs">
       <Link>Common\System\Collections\Generic\LargeArrayBuilder.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\System\IO\PathInternal.cs">
-      <Link>Common\System\IO\PathInternal.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)\System\IO\PathInternal.CaseSensitivity.cs">
       <Link>Common\System\IO\PathInternal.CaseSensitivity.cs</Link>
     </Compile>
@@ -82,9 +79,7 @@
     <Compile Include="Tests\System\StringExtensions.Tests.cs" />
     <Compile Include="Tests\System\Collections\Generic\ArrayBuilderTests.cs" />
     <Compile Include="Tests\System\Collections\Generic\LargeArrayBuilderTests.cs" />
-    <Compile Include="Tests\System\IO\PathInternal_Unix_Tests.cs" />
     <Compile Include="Tests\System\IO\RowConfigReaderTests.cs" />
-    <Compile Include="Tests\System\IO\PathInternal.Tests.cs" />
     <Compile Include="Tests\System\Net\HttpKnownHeaderNamesTests.cs" />
     <Compile Include="System\Net\Sockets\Fletcher32.cs">
       <Link>System\Net\Sockets\Fletcher32.cs</Link>
   </ItemGroup>
   <ItemGroup>
     <Folder Include="CommonTest\System\" />
+    <Folder Include="Common\System\Security\Cryptography\" />
     <Folder Include="System\Net\Sockets\" />
     <Folder Include="System\Net\VirtualNetwork\" />
   </ItemGroup>
   <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
+</Project>
\ 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 (file)
index 4435fb8..0000000
+++ /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));
-        }
-    }
-}
index 799dfc8..9d48b37 100644 (file)
@@ -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 (file)
index 5eac916..0000000
+++ /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);
-    }
-}
index 9cacdec..0353808 100644 (file)
@@ -58,9 +58,6 @@
     <Compile Include="System\IO\Compression\ZipArchiveEntry.netcoreapp.cs" />
     <Compile Include="System\IO\Compression\ZipCustomStreams.netcoreapp.cs" />
     <Compile Include="System\IO\Compression\PositionPreservingWriteOnlyStreamWrapper.netcoreapp.cs" />
-    <Compile Include="$(CommonPath)\System\IO\PathInternal.cs">
-      <Link>Common\System\IO\PathInternal.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)\System\IO\StreamHelpers.CopyValidation.cs">
       <Link>Common\System\IO\StreamHelpers.CopyValidation.cs</Link>
     </Compile>
index 056e32b..85f67d9 100644 (file)
@@ -20,9 +20,6 @@
     <Compile Include="$(CommonPath)\System\HResults.cs">
       <Link>Common\System\HResults.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\System\IO\PathInternal.cs">
-      <Link>Common\System\IO\PathInternal.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)\System\IO\PathInternal.CaseSensitivity.cs">
       <Link>Common\System\IO\PathInternal.CaseSensitivity.cs</Link>
     </Compile>
     <Reference Include="System.Diagnostics.Debug" />
     <Reference Include="System.Diagnostics.Tools" />
     <Reference Include="System.IO.FileSystem" />
+    <Reference Include="System.Memory" />
     <Reference Include="System.Resources.ResourceManager" />
     <Reference Include="System.Runtime" />
     <Reference Include="System.Runtime.Extensions" />
index 734563e..566798b 100644 (file)
@@ -29,9 +29,6 @@
     <Compile Include="System\IO\RenamedEventHandler.cs" />
     <Compile Include="System\IO\WatcherChangeTypes.cs" />
     <Compile Include="System\IO\WaitForChangedResult.cs" />
-    <Compile Include="$(CommonPath)\System\IO\PathInternal.cs">
-      <Link>Common\System\IO\PathInternal.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)\System\IO\PathInternal.CaseSensitivity.cs">
       <Link>Common\System\IO\PathInternal.CaseSensitivity.cs</Link>
     </Compile>
     <Reference Include="System.Diagnostics.Debug" />
     <Reference Include="System.Diagnostics.Tools" />
     <Reference Include="System.IO.FileSystem" />
+    <Reference Include="System.Memory" />
     <Reference Include="System.Resources.ResourceManager" />
     <Reference Include="System.Runtime" />
     <Reference Include="System.Runtime.Extensions" />
index 7da304a..5d90f6e 100644 (file)
@@ -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
index 93d318a..f6ddc7b 100644 (file)
@@ -44,9 +44,6 @@
     <Compile Include="$(CommonPath)\CoreLib\System\Text\ValueStringBuilder.cs">
       <Link>Common\CoreLib\System\Text\ValueStringBuilder.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\System\IO\PathInternal.cs">
-      <Link>Common\System\IO\PathInternal.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)\System\IO\PathInternal.CaseSensitivity.cs">
       <Link>Common\System\IO\PathInternal.CaseSensitivity.cs</Link>
     </Compile>
     <Compile Include="System\IO\Enumeration\FileSystemEnumerator.Windows.cs" />
     <Compile Include="System\IO\CharSpanExtensions.Windows.cs" />
     <Compile Include="System\IO\DisableMediaInsertionPrompt.cs" />
-    <Compile Include="System\IO\DirectoryInfo.Windows.cs" />
-    <Compile Include="System\IO\FileInfo.Windows.cs" />
     <Compile Include="System\IO\FileSystemInfo.Windows.cs" />
-    <Compile Include="System\IO\PathHelpers.Windows.cs" />
     <Compile Include="System\IO\Enumeration\FileSystemEntry.Windows.cs" />
     <Compile Include="System\IO\FileSystem.Windows.cs" />
     <Compile Include="Microsoft\Win32\SafeHandles\SafeFindHandle.Windows.cs" />
     <Compile Include="System\IO\Enumeration\FileSystemEntry.Unix.cs" />
     <Compile Include="System\IO\Enumeration\FileSystemEnumerator.Unix.cs" />
     <Compile Include="System\IO\CharSpanExtensions.Unix.cs" />
-    <Compile Include="System\IO\FileInfo.Unix.cs" />
-    <Compile Include="System\IO\DirectoryInfo.Unix.cs" />
     <Compile Include="System\IO\FileSystemInfo.Unix.cs" />
-    <Compile Include="System\IO\PathHelpers.Unix.cs" />
     <Compile Include="System\IO\FileSystem.Unix.cs" />
     <Compile Include="$(CommonPath)\Interop\Unix\Interop.Libraries.cs">
       <Link>Common\Interop\Unix\Interop.Libraries.cs</Link>
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 (file)
index 307df6c..0000000
+++ /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 (file)
index 84e26c6..0000000
+++ /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;
-        }
-    }
-}
index d4ba432..be18612 100644 (file)
@@ -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) ?
index 148364f..0d4d210 100644 (file)
@@ -17,7 +17,7 @@ namespace System.IO.Enumeration
         private ReadOnlySpan<char> _fileName;
         private fixed char _fileNameBuffer[FileNameBufferSize];
 
-        internal static bool Initialize(
+        internal static FileAttributes Initialize(
             ref FileSystemEntry entry,
             Interop.Sys.DirectoryEntry directoryEntry,
             ReadOnlySpan<char> directory,
@@ -33,9 +33,7 @@ namespace System.IO.Enumeration
             entry._fullPath = ReadOnlySpan<char>.Empty;
             entry._fileName = ReadOnlySpan<char>.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);
         }
 
         /// <summary>
index 48bf820..99292c5 100644 (file)
@@ -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);
 
         /// <summary>
         /// Returns the full path for find results, based on the initially provided path.
index ae1c5fb..4a0ccb8 100644 (file)
@@ -107,7 +107,8 @@ namespace System.IO.Enumeration
                     if (_lastEntryFound)
                         return false;
 
-                    bool isDirectory = FileSystemEntry.Initialize(ref entry, _entry, _currentPath, _rootDirectory, _originalRootDirectory, new Span<char>(_pathBuffer));
+                    FileAttributes attributes = FileSystemEntry.Initialize(ref entry, _entry, _currentPath, _rootDirectory, _originalRootDirectory, new Span<char>(_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 (file)
index e88bb9a..0000000
+++ /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 (file)
index 8820620..0000000
+++ /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;
-        }
-    }
-}
index c9a3ae5..387cc32 100644 (file)
@@ -37,6 +37,8 @@ namespace System.IO
 
         public FileAttributes GetAttributes(ReadOnlySpan<char> path, ReadOnlySpan<char> 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
index 1416544..c96aea1 100644 (file)
@@ -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)
index f45a713..e369567 100644 (file)
@@ -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 (file)
index 47dcd9b..0000000
+++ /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 (file)
index 3c30eaf..0000000
+++ /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<char> 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;
-    }
-}
index 5e6a884..cb2ce24 100644 (file)
@@ -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<char> 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<char> 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<char> TrimEndingDirectorySeparator(ReadOnlySpan<char> path) =>
+            EndsInDirectorySeparator(path) && !IsRoot(path) ?
+                path.Slice(0, path.Length - 1) :
+                path;
 
         /// <summary>
         /// Combines two paths. Does no validation of paths, only concatenates the paths
index c5f4f02..1a710ac 100644 (file)
@@ -6,6 +6,7 @@
     <ProjectGuid>{187503F4-BEF9-4369-A1B2-E3DC5D564E4E}</ProjectGuid>
     <IsPartialFacadeAssembly Condition="'$(TargetGroup)' == 'netfx'">true</IsPartialFacadeAssembly>
     <GeneratePlatformNotSupportedAssemblyMessage Condition="'$(TargetGroup)' == 'netstandard' AND '$(TargetsWindows)' != 'true'">SR.PlatformNotSupported_IOPorts</GeneratePlatformNotSupportedAssemblyMessage>
+    <DefineConstants>$(DefineConstants);NOSPAN</DefineConstants>
     <IncludeDllSafeSearchPathAttribute>true</IncludeDllSafeSearchPathAttribute>
   </PropertyGroup>
   <!-- Default configurations to help VS understand the options -->
index 3178bc3..718d19f 100644 (file)
@@ -79,9 +79,6 @@
     <Compile Include="$(CommonPath)\System\IO\StringBuilderCache.cs">
       <Link>Common\System\IO\StringBuilderCache.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\System\IO\PathInternal.cs">
-      <Link>Common\System\IO\PathInternal.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)\System\HResults.cs">
       <Link>Common\System\HResults.cs</Link>
     </Compile>
     <Compile Include="$(CommonPath)\System\IO\DriveInfoInternal.Win32.cs">
       <Link>Common\System\IO\DriveInfoInternal.Win32.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\System\IO\PathInternal.Windows.cs">
-      <Link>Common\System\IO\PathInternal.Windows.cs</Link>
-    </Compile>
   </ItemGroup>
   <!-- UNIX -->
   <ItemGroup Condition=" '$(TargetsUnix)' == 'true' ">
     <Compile Include="$(CommonPath)\System\IO\DriveInfoInternal.Unix.cs">
       <Link>Common\System\IO\DriveInfoInternal.Unix.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\System\IO\PathInternal.Unix.cs">
-      <Link>Common\System\IO\PathInternal.Unix.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)\System\IO\PersistedFiles.Unix.cs">
       <Link>Common\System\IO\PersistedFiles.Unix.cs</Link>
     </Compile>
     <ReferenceFromRuntime Include="System.Private.CoreLib" />
   </ItemGroup>
   <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
+</Project>
\ No newline at end of file
index 2caff31..a835b07 100644 (file)
@@ -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;
index 833ed29..448b434 100644 (file)
@@ -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
 {