From 81349ba279302d26d566863eb6277216c772dc80 Mon Sep 17 00:00:00 2001
From: Tarun <gtarun047@gmail.com>
Date: Wed, 26 Jan 2022 15:15:32 +0530
Subject: [PATCH 01/11] Add APIs to check if a given path exists

---
 .../src/System/IO/Path.Unix.cs                      | 12 ++++++++++++
 .../src/System/IO/Path.Windows.cs                   | 13 +++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
index f3ec19941c21b2..8826561803cb19 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
@@ -13,6 +13,18 @@ public static partial class Path
 
         public static char[] GetInvalidPathChars() => new char[] { '\0' };
 
+        // Checks if the given path is available for use.
+        public static bool Exists([NotNullWhen(true)] string? path)
+        {
+            if (path == null)
+            {
+                return false;
+            }
+
+            ReadOnlySpan<char> fullPath = GetFullPath(path).AsSpan();
+            return Interop.Sys.LStat(fullPath, out Interop.Sys.FileStatus _) > 0;
+        }
+
         // Expands the given path to a fully qualified path.
         public static string GetFullPath(string path)
         {
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
index b10cedd4ecbdd2..4c681dcb4a6a83 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
@@ -27,6 +27,19 @@ public static partial class Path
             (char)31
         };
 
+        //Checks if the given path is available for use.
+        public static bool Exists([NotNullWhen(true)] string? path)
+        {
+            if (path == null)
+            {
+                return false;
+            }
+
+            var fullPath = GetFullPath(path);
+            Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = default;
+            return FileSystem.FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true) == 0;
+        }
+
         // Expands the given path to a fully qualified path.
         public static string GetFullPath(string path)
         {

From 430bb30a37cdaccb4173907c3a4408401b341271 Mon Sep 17 00:00:00 2001
From: Tarun047 <gtarun047@gmail.com>
Date: Wed, 26 Jan 2022 23:40:55 +0530
Subject: [PATCH 02/11] Add Tests for File.Exists API

---
 .../System.IO.FileSystem/tests/Path/Exists.cs | 507 ++++++++++++++++++
 .../tests/System.IO.FileSystem.Tests.csproj   |   1 +
 .../src/System/IO/Path.Unix.cs                |  22 +-
 .../src/System/IO/Path.Windows.cs             |  24 +-
 .../System.Runtime/ref/System.Runtime.cs      |   1 +
 5 files changed, 546 insertions(+), 9 deletions(-)
 create mode 100644 src/libraries/System.IO.FileSystem/tests/Path/Exists.cs

diff --git a/src/libraries/System.IO.FileSystem/tests/Path/Exists.cs b/src/libraries/System.IO.FileSystem/tests/Path/Exists.cs
new file mode 100644
index 00000000000000..6fe69836f499ce
--- /dev/null
+++ b/src/libraries/System.IO.FileSystem/tests/Path/Exists.cs
@@ -0,0 +1,507 @@
+﻿// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using Xunit;
+
+namespace System.IO.Tests
+{
+    public class Path_Exists : FileSystemTest
+    {
+
+        private bool Exists(string path)
+        {
+            return Path.Exists(path);
+        }
+
+        #region Common Tests
+        [Fact]
+        public void NullAsPath_ReturnsFalse()
+        {
+            Assert.False(Exists(null));
+        }
+
+        [Fact]
+        public void EmptyAsPath_ReturnsFalse()
+        {
+            Assert.False(Exists(string.Empty));
+        }
+
+        [Theory,
+           MemberData(nameof(ValidPathComponentNames))]
+        public void NonExistentValidPath_ReturnsFalse(string path)
+        {
+            Assert.False(Exists(path), path);
+        }
+
+        [Theory, MemberData(nameof(PathsWithInvalidCharacters))]
+        public void PathWithInvalidCharactersAsPath_ReturnsFalse(string invalidPath)
+        {
+            // Checks that errors aren't thrown when calling Exists() on paths with impossible to create characters
+            Assert.False(Exists(invalidPath));
+
+            // Checks that errors aren't thrown when calling Exists() on paths with impossible to create characters
+            char[] trimmed = { (char)0x9, (char)0xA, (char)0xB, (char)0xC, (char)0xD, (char)0x20, (char)0x85, (char)0xA0 };
+            if (!trimmed.Contains(invalidPath.ToCharArray()[0]))
+                Assert.False(Exists(TestDirectory + Path.DirectorySeparatorChar + invalidPath));
+
+        }
+
+
+        #endregion
+
+        #region File Tests
+
+        [Theory,
+           MemberData(nameof(ValidPathComponentNames))]
+        public void ValidFilePathExists_ReturnsTrue(string component)
+        {
+            string path = Path.Combine(TestDirectory, component);
+            FileInfo testFile = new FileInfo(path);
+            testFile.Create().Dispose();
+            Assert.True(Exists(path));
+        }
+
+        [Fact]
+        public void PathAlreadyExistsAsFile()
+        {
+            string path = GetTestFilePath();
+            File.Create(path).Dispose();
+
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(path)));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
+        }
+
+        [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))]
+        public void FileSymLinksMayExistIndependentlyOfTarget()
+        {
+            var path = GetTestFilePath();
+            var linkPath = GetRandomLinkPath();
+
+            File.Create(path).Dispose();
+            Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: false));
+
+            // Both the symlink and the target exist
+            Assert.True(File.Exists(path), "path should exist");
+            Assert.True(File.Exists(linkPath), "linkPath should exist");
+
+            // Delete the target.  The symlink should still exist
+            File.Delete(path);
+            Assert.False(File.Exists(path), "path should now not exist");
+            Assert.True(File.Exists(linkPath), "linkPath should still exist");
+
+            // Now delete the symlink.
+            File.Delete(linkPath);
+            Assert.False(File.Exists(linkPath), "linkPath should no longer exist");
+        }
+
+        #endregion
+
+        #region Directory Tests
+
+
+        [Theory,
+            MemberData(nameof(ValidPathComponentNames))]
+        public void ValidDirectoryPathExists_ReturnsTrue(string component)
+        {
+            string path = Path.Combine(TestDirectory, component);
+            Directory.CreateDirectory(path);
+            Assert.True(Exists(path));
+        }
+
+        [Fact]
+        public void PathAlreadyExistsAsDirectory()
+        {
+            string path = GetTestFilePath();
+            Directory.CreateDirectory(path);
+
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(path)));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
+        }
+
+        [Fact]
+        public void DotAsPath_ReturnsTrue()
+        {
+            Assert.True(Exists(Path.Combine(TestDirectory, ".")));
+        }
+
+        [Fact]
+        public void DirectoryGetCurrentDirectoryAsPath_ReturnsTrue()
+        {
+            Assert.True(Exists(Directory.GetCurrentDirectory()));
+        }
+
+        [Fact]
+        public void DotDotAsPath_ReturnsTrue()
+        {
+            Assert.True(Exists(Path.Combine(TestDirectory, GetTestFileName(), "..")));
+        }
+
+        [Fact]
+        public void DirectoryLongerThanMaxLongPath_DoesntThrow()
+        {
+            Assert.All((IOInputs.GetPathsLongerThanMaxLongPath(GetTestFilePath())), (path) =>
+            {
+                Assert.False(Exists(path), path);
+            });
+        }
+
+        [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))]
+        public void DirectorySymLinksMayExistIndependentlyOfTarget()
+        {
+            var path = GetTestFilePath();
+            var linkPath = GetRandomLinkPath();
+
+            Directory.CreateDirectory(path);
+            Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: true));
+
+            // Both the symlink and the target exist
+            Assert.True(Directory.Exists(path), "path should exist");
+            Assert.True(Directory.Exists(linkPath), "linkPath should exist");
+            Assert.False(File.Exists(linkPath));
+
+            // Delete the target.  The symlink should still exist.  On Unix, the symlink will now be
+            // considered a file (since it's broken and we don't know what it'll eventually point to).
+            Directory.Delete(path);
+            Assert.False(Directory.Exists(path), "path should now not exist");
+            if (OperatingSystem.IsWindows())
+            {
+                Assert.True(Directory.Exists(linkPath), "linkPath should still exist as a directory");
+                Assert.False(File.Exists(linkPath), "linkPath should not be a file");
+            }
+            else
+            {
+                Assert.False(Directory.Exists(linkPath), "linkPath should no longer be a directory");
+                Assert.True(File.Exists(linkPath), "linkPath should now be a file");
+            }
+
+            // Now delete the symlink.
+            // On Unix, deleting the symlink should fail, because it's not a directory, it's a file.
+            // On Windows, it should succeed.
+            try
+            {
+                Directory.Delete(linkPath);
+                Assert.True(OperatingSystem.IsWindows(), "Should only succeed on Windows");
+            }
+            catch (IOException)
+            {
+                Assert.False(OperatingSystem.IsWindows(), "Should only fail on Unix");
+                File.Delete(linkPath);
+            }
+
+            Assert.False(Directory.Exists(linkPath), "linkPath should no longer exist as a directory");
+            Assert.False(File.Exists(linkPath), "linkPath should no longer exist as a file");
+        }
+
+        [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))]
+        public void SymlinkToNewDirectory()
+        {
+            string path = GetTestFilePath();
+            Directory.CreateDirectory(path);
+
+            string linkPath = GetRandomLinkPath();
+            Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: true));
+
+            Assert.True(Directory.Exists(path));
+            Assert.True(Directory.Exists(linkPath));
+        }
+
+        #endregion
+
+        #region Platform Specific Common Tests
+        [Theory,
+           MemberData(nameof(WhiteSpace))]
+        [PlatformSpecific(TestPlatforms.Windows)] // alternate data stream
+        public void PathWithAlternateDataStreams_ReturnsFalse(string component)
+        {
+            Assert.False(Exists(component));
+
+        }
+
+        [ConditionalTheory(nameof(ReservedDeviceNamesAreBlocked))] // device names
+        [MemberData(nameof(PathsWithReservedDeviceNames))]
+        [OuterLoop]
+        public void PathWithReservedDeviceNameAsPath_ReturnsFalse(string component)
+        {
+            Assert.False(Exists(component));
+        }
+
+        [ActiveIssue("https://github.com/dotnet/runtimelab/issues/901", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+        [Theory,
+            MemberData(nameof(UncPathsWithoutShareName))]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/51371", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
+        public void UncPathWithoutShareNameAsPath_ReturnsFalse(string component)
+        {
+            Assert.False(Exists(component));
+        }
+
+        [Theory,
+          MemberData(nameof(PathsWithComponentLongerThanMaxComponent))]
+        [PlatformSpecific(TestPlatforms.Windows)] // max directory length not fixed on Unix
+        public void DirectoryWithComponentLongerThanMaxComponentAsPath_ReturnsFalse(string component)
+        {
+            Assert.False(Exists(component));
+        }
+        [Fact]
+        [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)]  // Makes call to native code (libc)
+        public void FalseForNonRegularFile()
+        {
+            string fileName = GetTestFilePath();
+            Assert.Equal(0, mkfifo(fileName, 0));
+            Assert.False(Directory.Exists(fileName));
+        }
+        #endregion
+
+        #region Platform Specific File Tests
+
+        [Theory,
+            MemberData(nameof(WhiteSpace))]
+        [PlatformSpecific(TestPlatforms.Windows)] // Unix equivalent tested already in CreateDirectory
+        public void WindowsNonSignificantWhiteSpaceAsPath_ReturnsFalse(string component)
+        {
+            // Checks that errors aren't thrown when calling Exists() on impossible paths
+            Assert.False(Exists(component));
+
+        }
+
+        [Fact]
+        [PlatformSpecific(CaseInsensitivePlatforms)]
+        public void DoesCaseInsensitiveInvariantFileComparions()
+        {
+            FileInfo testFile = new FileInfo(GetTestFilePath());
+            testFile.Create().Dispose();
+            Assert.True(Exists(testFile.FullName));
+            Assert.True(Exists(testFile.FullName.ToUpperInvariant()));
+            Assert.True(Exists(testFile.FullName.ToLowerInvariant()));
+        }
+
+        [Fact]
+        [PlatformSpecific(CaseSensitivePlatforms)]
+        public void DoesCaseSensitiveFileComparisons()
+        {
+            FileInfo testFile = new FileInfo(GetTestFilePath());
+            testFile.Create().Dispose();
+            Assert.True(Exists(testFile.FullName));
+            Assert.False(Exists(testFile.FullName.ToUpperInvariant()));
+            Assert.False(Exists(testFile.FullName.ToLowerInvariant()));
+        }
+
+        [Theory,
+           MemberData(nameof(NonControlWhiteSpace))]
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public void TrailingWhiteSpace_NotTrimmed(string component)
+        {
+            // In .NET Core we don't trim anything other than space (' ')
+            string path = GetTestFilePath() + component;
+            FileInfo testFile = new FileInfo(path);
+            testFile.Create().Dispose();
+
+            Assert.True(Exists(path));
+        }
+
+        [Theory,
+           MemberData(nameof(SimpleWhiteSpace))] //*Just* spaces
+        [PlatformSpecific(TestPlatforms.Windows)] // In Windows, trailing whitespace in a path is trimmed
+        public void TrailingSpace_Trimmed(string component)
+        {
+            FileInfo testFile = new FileInfo(GetTestFilePath());
+            testFile.Create().Dispose();
+
+            // Windows will trim trailing spaces
+            Assert.True(Exists(testFile.FullName + component));
+        }
+        #endregion
+
+        #region Platform Specific Directory Tests
+
+        [ConditionalTheory(nameof(UsingNewNormalization)),
+            MemberData(nameof(ValidPathComponentNames))]
+        [PlatformSpecific(TestPlatforms.Windows)]  // Extended path exists
+        public void ValidExtendedPathExists_ReturnsTrue(string component)
+        {
+            string path = IOInputs.ExtendedPrefix + Path.Combine(TestDirectory, "extended", component);
+            Directory.CreateDirectory(path);
+            Assert.True(Exists(path));
+        }
+
+        [ConditionalFact(nameof(UsingNewNormalization))]
+        [PlatformSpecific(TestPlatforms.Windows)]  // Extended path already exists as file
+        public void ExtendedPathAlreadyExistsAsFile()
+        {
+            string path = IOInputs.ExtendedPrefix + GetTestFilePath();
+            File.Create(path).Dispose();
+
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(path)));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
+        }
+
+        [ConditionalFact(nameof(UsingNewNormalization))]
+        [PlatformSpecific(TestPlatforms.Windows)]  // Extended path already exists as directory
+        public void ExtendedPathAlreadyExistsAsDirectory()
+        {
+            string path = IOInputs.ExtendedPrefix + GetTestFilePath();
+            Directory.CreateDirectory(path);
+
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(path)));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
+        }
+
+        [ConditionalFact(nameof(AreAllLongPathsAvailable))]
+        [PlatformSpecific(TestPlatforms.Windows)]  // Long directory path doesn't throw on Exists
+        public void DirectoryLongerThanMaxDirectoryAsPath_DoesntThrow()
+        {
+            Assert.All((IOInputs.GetPathsLongerThanMaxDirectory(GetTestFilePath())), (path) =>
+            {
+                Assert.False(Exists(path));
+            });
+        }
+
+        [Theory,
+            MemberData(nameof(WhiteSpace))]
+        [PlatformSpecific(TestPlatforms.Windows)] // Unix equivalent tested already in CreateDirectory
+        public void WindowsWhiteSpaceAsPath_ReturnsFalse(string component)
+        {
+            // Checks that errors aren't thrown when calling Exists() on impossible paths
+            Assert.False(Exists(component));
+
+        }
+
+        [Fact]
+        [PlatformSpecific(CaseInsensitivePlatforms)]
+        public void DoesCaseInsensitiveInvariantDirectoryComparisons()
+        {
+            DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
+            Assert.True(Exists(testDir.FullName));
+            Assert.True(Exists(testDir.FullName.ToUpperInvariant()));
+            Assert.True(Exists(testDir.FullName.ToLowerInvariant()));
+        }
+
+        [Fact]
+        [PlatformSpecific(CaseSensitivePlatforms)]
+        public void DoesCaseSensitiveDirectoryComparisons()
+        {
+            DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
+            Assert.True(Exists(testDir.FullName));
+            Assert.False(Exists(testDir.FullName.ToUpperInvariant()));
+            Assert.False(Exists(testDir.FullName.ToLowerInvariant()));
+        }
+
+        [ConditionalTheory(nameof(UsingNewNormalization)),
+            MemberData(nameof(SimpleWhiteSpace))]
+        [PlatformSpecific(TestPlatforms.Windows)] // In Windows, trailing whitespace in a path is trimmed appropriately
+        public void TrailingWhitespaceExistence_SimpleWhiteSpace(string component)
+        {
+            // This test relies on \\?\ support
+
+            DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
+
+            string path = GetTestFilePath(memberName: "Extended") + component;
+            testDir = Directory.CreateDirectory(IOInputs.ExtendedPrefix + path);
+            Assert.False(Exists(path), path);
+            Assert.True(Exists(testDir.FullName));
+        }
+
+        [Theory,
+            MemberData(nameof(NonControlWhiteSpace))]
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public void NonControlWhiteSpaceExists(string component)
+        {
+            DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath() + component);
+
+            string path = testDir.FullName;
+            Assert.True(Exists(path), "directory with non control whitespace should exist");
+        }
+
+        [Theory,
+            MemberData(nameof(SimpleWhiteSpace))] // *Just spaces*
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public void TrailingSpaceExists(string component)
+        {
+            // Windows trims spaces
+            string path = GetTestFilePath();
+            Directory.CreateDirectory(path + component);
+            Assert.True(Exists(path), "can find without space");
+            Assert.True(Exists(path + component), "can find with space");
+        }
+
+        [Fact]
+        [PlatformSpecific(TestPlatforms.Windows)] // max directory length not fixed on Unix
+        public void DirectoryEqualToMaxDirectory_ReturnsTrue()
+        {
+            // Creates directories up to the maximum directory length all at once
+            DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
+            string path = IOServices.GetPath(testDir.FullName, IOInputs.MaxDirectory);
+            Directory.CreateDirectory(path);
+            Assert.True(Exists(path));
+        }
+
+        [Fact]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/14378")]
+        [PlatformSpecific(TestPlatforms.Windows)] // drive labels
+        public void NotReadyDriveAsPath_ReturnsFalse()
+        {
+            var drive = IOServices.GetNotReadyDrive();
+            if (drive == null)
+            {
+                Console.WriteLine("Skipping test. Unable to find a not-ready drive, such as CD-Rom with no disc inserted.");
+                return;
+            }
+
+            bool result = Exists(drive);
+
+            Assert.False(result);
+        }
+
+        [Fact]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/14378")]
+        [PlatformSpecific(TestPlatforms.Windows)] // drive labels
+        public void SubdirectoryOnNotReadyDriveAsPath_ReturnsFalse()
+        {
+            var drive = IOServices.GetNotReadyDrive();
+            if (drive == null)
+            {
+                Console.WriteLine("Skipping test. Unable to find a not-ready drive, such as CD-Rom with no disc inserted.");
+                return;
+            }
+
+            bool result = Exists(Path.Combine(drive, "Subdirectory"));
+
+            Assert.False(result);
+        }
+
+        // Not all drives may be accessible (locked, no rights, etc.), and as such would return false.
+        // eg. Create a new volume, bitlocker it, and lock it. This new volume is no longer accessible
+        // and any attempt to access this drive will return false.
+        // We just care that we can access an accessible drive directly, we don't care which one.
+        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInAppContainer))] // Can't read root in appcontainer
+        [PlatformSpecific(TestPlatforms.Windows)] // drive labels
+        public void DriveAsPath()
+        {
+            Assert.False(Exists(IOServices.GetNonExistentDrive()));
+            Assert.Contains(IOServices.GetReadyDrives(), drive => Exists(drive));
+        }
+
+        [ConditionalFact(nameof(UsingNewNormalization))]
+        [PlatformSpecific(TestPlatforms.Windows)] // drive labels
+        public void ExtendedDriveAsPath()
+        {
+            Assert.False(Exists(IOInputs.ExtendedPrefix + IOServices.GetNonExistentDrive()));
+
+            if (PlatformDetection.IsNotInAppContainer)
+                Assert.Contains(IOServices.GetReadyDrives(), drive => Exists(IOInputs.ExtendedPrefix + drive));
+        }
+
+        [Fact]
+        [PlatformSpecific(TestPlatforms.Windows)] // drive labels
+        public void SubdirectoryOnNonExistentDriveAsPath_ReturnsFalse()
+        {
+            Assert.False(Exists(Path.Combine(IOServices.GetNonExistentDrive(), "nonexistentsubdir")));
+        }
+
+        #endregion
+    }
+}
diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
index 83f7bf47ea78e0..78b42f58ba60c8 100644
--- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
+++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
@@ -58,6 +58,7 @@
     <Compile Include="Enumeration\SymbolicLinksTests.cs" />
     <Compile Include="LargeFileTests.cs" />
     <Compile Include="PathInternalTests.cs" />
+    <Compile Include="Path\Exists.cs" />
     <Compile Include="RandomAccess\Base.cs" />
     <Compile Include="RandomAccess\GetLength.cs" />
     <Compile Include="RandomAccess\Read.cs" />
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
index 8826561803cb19..4905c2b153646c 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
@@ -16,13 +16,27 @@ public static partial class Path
         // Checks if the given path is available for use.
         public static bool Exists([NotNullWhen(true)] string? path)
         {
-            if (path == null)
+            try
             {
-                return false;
+                if (path == null)
+                {
+                    return false;
+                }
+
+                if (path.Length == 0)
+                {
+                    return false;
+                }
+
+                ReadOnlySpan<char> fullPath = GetFullPath(path).AsSpan();
+                return Interop.Sys.LStat(fullPath, out Interop.Sys.FileStatus _) > 0;
             }
 
-            ReadOnlySpan<char> fullPath = GetFullPath(path).AsSpan();
-            return Interop.Sys.LStat(fullPath, out Interop.Sys.FileStatus _) > 0;
+            catch (ArgumentException) { }
+            catch (IOException) { }
+            catch (UnauthorizedAccessException) { }
+
+            return false;
         }
 
         // Expands the given path to a fully qualified path.
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
index 4c681dcb4a6a83..cd6115577f66b2 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
@@ -30,14 +30,28 @@ public static partial class Path
         //Checks if the given path is available for use.
         public static bool Exists([NotNullWhen(true)] string? path)
         {
-            if (path == null)
+            try
             {
-                return false;
+                if (path == null)
+                {
+                    return false;
+                }
+
+                if (path.Length == 0)
+                {
+                    return false;
+                }
+
+                string fullPath = GetFullPath(path);
+                Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = default;
+                return FileSystem.FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true) == 0;
             }
 
-            var fullPath = GetFullPath(path);
-            Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = default;
-            return FileSystem.FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true) == 0;
+            catch (ArgumentException) { }
+            catch (IOException) { }
+            catch (UnauthorizedAccessException) { }
+
+            return false;
         }
 
         // Expands the given path to a fully qualified path.
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index 82211a2cca7c8d..3cb55b8bc698f1 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -10713,6 +10713,7 @@ public static partial class Path
         public static string Combine(params string[] paths) { throw null; }
         public static bool EndsInDirectorySeparator(System.ReadOnlySpan<char> path) { throw null; }
         public static bool EndsInDirectorySeparator(string path) { throw null; }
+        public static bool Exists([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? path) { throw null; }
         public static System.ReadOnlySpan<char> GetDirectoryName(System.ReadOnlySpan<char> path) { throw null; }
         public static string? GetDirectoryName(string? path) { throw null; }
         public static System.ReadOnlySpan<char> GetExtension(System.ReadOnlySpan<char> path) { throw null; }

From b967addef96c78238bc9c206c9c1847d27697b2d Mon Sep 17 00:00:00 2001
From: Tarun047 <gtarun047@gmail.com>
Date: Thu, 27 Jan 2022 20:25:57 +0530
Subject: [PATCH 03/11] Update Code to reuse Tests and Address PR Comments

---
 .../tests/Directory/Exists.cs                 |  59 +-
 .../System.IO.FileSystem/tests/File/Exists.cs |  78 +--
 .../System.IO.FileSystem/tests/Path/Exists.cs | 507 ------------------
 .../tests/Path/Exists_Directory.cs            |  43 ++
 .../tests/Path/Exists_File.cs                 |  49 ++
 .../tests/System.IO.FileSystem.Tests.csproj   |   3 +-
 .../src/System/IO/Path.Unix.cs                |   9 +-
 .../src/System/IO/Path.Windows.cs             |  13 +-
 8 files changed, 182 insertions(+), 579 deletions(-)
 delete mode 100644 src/libraries/System.IO.FileSystem/tests/Path/Exists.cs
 create mode 100644 src/libraries/System.IO.FileSystem/tests/Path/Exists_Directory.cs
 create mode 100644 src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs

diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs b/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs
index a05fe8ad41f7dc..856f1d62408240 100644
--- a/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs
+++ b/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs
@@ -10,7 +10,7 @@ public class Directory_Exists : FileSystemTest
     {
         #region Utilities
 
-        public bool Exists(string path)
+        public virtual bool Exists(string path)
         {
             return Directory.Exists(path);
         }
@@ -57,17 +57,6 @@ public void PathWithInvalidCharactersAsPath_ReturnsFalse(string invalidPath)
                 Assert.False(Exists(TestDirectory + Path.DirectorySeparatorChar + invalidPath));
         }
 
-        [Fact]
-        public void PathAlreadyExistsAsFile()
-        {
-            string path = GetTestFilePath();
-            File.Create(path).Dispose();
-
-            Assert.False(Exists(IOServices.RemoveTrailingSlash(path)));
-            Assert.False(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
-            Assert.False(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
-        }
-
         [Fact]
         public void PathAlreadyExistsAsDirectory()
         {
@@ -180,18 +169,6 @@ public void ValidExtendedPathExists_ReturnsTrue(string component)
             Assert.True(Exists(path));
         }
 
-        [ConditionalFact(nameof(UsingNewNormalization))]
-        [PlatformSpecific(TestPlatforms.Windows)]  // Extended path already exists as file
-        public void ExtendedPathAlreadyExistsAsFile()
-        {
-            string path = IOInputs.ExtendedPrefix + GetTestFilePath();
-            File.Create(path).Dispose();
-
-            Assert.False(Exists(IOServices.RemoveTrailingSlash(path)));
-            Assert.False(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
-            Assert.False(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
-        }
-
         [ConditionalFact(nameof(UsingNewNormalization))]
         [PlatformSpecific(TestPlatforms.Windows)]  // Extended path already exists as directory
         public void ExtendedPathAlreadyExistsAsDirectory()
@@ -390,6 +367,40 @@ public void SubdirectoryOnNonExistentDriveAsPath_ReturnsFalse()
             Assert.False(Exists(Path.Combine(IOServices.GetNonExistentDrive(), "nonexistentsubdir")));
         }
 
+        #endregion
+    }
+
+    public class Directory_ExistsAsFile : FileSystemTest
+    {
+        #region Universal
+
+        [Fact]
+        public void PathAlreadyExistsAsFile()
+        {
+            string path = GetTestFilePath();
+            File.Create(path).Dispose();
+
+            Assert.False(Directory.Exists(IOServices.RemoveTrailingSlash(path)));
+            Assert.False(Directory.Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
+            Assert.False(Directory.Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
+        }
+
+        #endregion
+
+        #region PlatformSpecific
+
+        [ConditionalFact(nameof(UsingNewNormalization))]
+        [PlatformSpecific(TestPlatforms.Windows)]  // Extended path already exists as file
+        public void ExtendedPathAlreadyExistsAsFile()
+        {
+            string path = IOInputs.ExtendedPrefix + GetTestFilePath();
+            File.Create(path).Dispose();
+
+            Assert.False(Directory.Exists(IOServices.RemoveTrailingSlash(path)));
+            Assert.False(Directory.Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
+            Assert.False(Directory.Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
+        }
+
         [Fact]
         [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)]  // Makes call to native code (libc)
         public void FalseForNonRegularFile()
diff --git a/src/libraries/System.IO.FileSystem/tests/File/Exists.cs b/src/libraries/System.IO.FileSystem/tests/File/Exists.cs
index d35ba99eeb2ab5..d43497e43b1a79 100644
--- a/src/libraries/System.IO.FileSystem/tests/File/Exists.cs
+++ b/src/libraries/System.IO.FileSystem/tests/File/Exists.cs
@@ -52,9 +52,6 @@ public void PathWithInvalidCharactersAsPath_ReturnsFalse(string invalidPath)
         {
             // Checks that errors aren't thrown when calling Exists() on paths with impossible to create characters
             Assert.False(Exists(invalidPath));
-
-            Assert.False(Exists(".."));
-            Assert.False(Exists("."));
         }
 
         [Fact]
@@ -82,35 +79,6 @@ public void PathEndsInAltTrailingSlash_Windows()
             string path = GetTestFilePath() + Path.DirectorySeparatorChar;
             Assert.False(Exists(path));
         }
-
-        [Fact]
-        public void PathEndsInTrailingSlash_AndExists()
-        {
-            string path = GetTestFilePath();
-            File.Create(path).Dispose();
-            Assert.False(Exists(path + Path.DirectorySeparatorChar));
-        }
-
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)]
-        public void PathEndsInAltTrailingSlash_AndExists_Windows()
-        {
-            string path = GetTestFilePath();
-            File.Create(path).Dispose();
-            Assert.False(Exists(path + Path.DirectorySeparatorChar));
-        }
-
-        [Fact]
-        public void PathAlreadyExistsAsDirectory()
-        {
-            string path = GetTestFilePath();
-            Directory.CreateDirectory(path);
-
-            Assert.False(Exists(IOServices.RemoveTrailingSlash(path)));
-            Assert.False(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
-            Assert.False(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
-        }
-
         [Fact]
         [ActiveIssue("https://github.com/dotnet/runtime/issues/51371", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
         public void DirectoryLongerThanMaxDirectoryAsPath_DoesntThrow()
@@ -248,6 +216,43 @@ public void DirectoryWithComponentLongerThanMaxComponentAsPath_ReturnsFalse(stri
             Assert.False(Exists(component));
         }
 
+        #endregion
+    }
+
+    public class File_ExistsAsDirectory : FileSystemTest
+    {
+        #region Universal
+
+        [Fact]
+        public void DotAsPathReturnsFalse()
+        {
+            Assert.False(File.Exists("."));
+            Assert.False(File.Exists(".."));
+        }
+
+        [Fact]
+        public void PathEndsInTrailingSlash_AndExists()
+        {
+            string path = GetTestFilePath();
+            File.Create(path).Dispose();
+            Assert.False(File.Exists(path + Path.DirectorySeparatorChar));
+        }
+
+        [Fact]
+        public void PathAlreadyExistsAsDirectory()
+        {
+            string path = GetTestFilePath();
+            Directory.CreateDirectory(path);
+
+            Assert.False(File.Exists(IOServices.RemoveTrailingSlash(path)));
+            Assert.False(File.Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
+            Assert.False(File.Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
+        }
+
+        #endregion
+
+        #region PlatformSpecific
+
         [Fact]
         [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)]  // Uses P/Invokes
         public void FalseForNonRegularFile()
@@ -257,6 +262,15 @@ public void FalseForNonRegularFile()
             Assert.True(File.Exists(fileName));
         }
 
+        [Fact]
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public void PathEndsInAltTrailingSlash_AndExists_Windows()
+        {
+            string path = GetTestFilePath();
+            File.Create(path).Dispose();
+            Assert.False(File.Exists(path + Path.DirectorySeparatorChar));
+        }
+
         #endregion
     }
 }
diff --git a/src/libraries/System.IO.FileSystem/tests/Path/Exists.cs b/src/libraries/System.IO.FileSystem/tests/Path/Exists.cs
deleted file mode 100644
index 6fe69836f499ce..00000000000000
--- a/src/libraries/System.IO.FileSystem/tests/Path/Exists.cs
+++ /dev/null
@@ -1,507 +0,0 @@
-﻿// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Linq;
-using Xunit;
-
-namespace System.IO.Tests
-{
-    public class Path_Exists : FileSystemTest
-    {
-
-        private bool Exists(string path)
-        {
-            return Path.Exists(path);
-        }
-
-        #region Common Tests
-        [Fact]
-        public void NullAsPath_ReturnsFalse()
-        {
-            Assert.False(Exists(null));
-        }
-
-        [Fact]
-        public void EmptyAsPath_ReturnsFalse()
-        {
-            Assert.False(Exists(string.Empty));
-        }
-
-        [Theory,
-           MemberData(nameof(ValidPathComponentNames))]
-        public void NonExistentValidPath_ReturnsFalse(string path)
-        {
-            Assert.False(Exists(path), path);
-        }
-
-        [Theory, MemberData(nameof(PathsWithInvalidCharacters))]
-        public void PathWithInvalidCharactersAsPath_ReturnsFalse(string invalidPath)
-        {
-            // Checks that errors aren't thrown when calling Exists() on paths with impossible to create characters
-            Assert.False(Exists(invalidPath));
-
-            // Checks that errors aren't thrown when calling Exists() on paths with impossible to create characters
-            char[] trimmed = { (char)0x9, (char)0xA, (char)0xB, (char)0xC, (char)0xD, (char)0x20, (char)0x85, (char)0xA0 };
-            if (!trimmed.Contains(invalidPath.ToCharArray()[0]))
-                Assert.False(Exists(TestDirectory + Path.DirectorySeparatorChar + invalidPath));
-
-        }
-
-
-        #endregion
-
-        #region File Tests
-
-        [Theory,
-           MemberData(nameof(ValidPathComponentNames))]
-        public void ValidFilePathExists_ReturnsTrue(string component)
-        {
-            string path = Path.Combine(TestDirectory, component);
-            FileInfo testFile = new FileInfo(path);
-            testFile.Create().Dispose();
-            Assert.True(Exists(path));
-        }
-
-        [Fact]
-        public void PathAlreadyExistsAsFile()
-        {
-            string path = GetTestFilePath();
-            File.Create(path).Dispose();
-
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(path)));
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
-        }
-
-        [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))]
-        public void FileSymLinksMayExistIndependentlyOfTarget()
-        {
-            var path = GetTestFilePath();
-            var linkPath = GetRandomLinkPath();
-
-            File.Create(path).Dispose();
-            Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: false));
-
-            // Both the symlink and the target exist
-            Assert.True(File.Exists(path), "path should exist");
-            Assert.True(File.Exists(linkPath), "linkPath should exist");
-
-            // Delete the target.  The symlink should still exist
-            File.Delete(path);
-            Assert.False(File.Exists(path), "path should now not exist");
-            Assert.True(File.Exists(linkPath), "linkPath should still exist");
-
-            // Now delete the symlink.
-            File.Delete(linkPath);
-            Assert.False(File.Exists(linkPath), "linkPath should no longer exist");
-        }
-
-        #endregion
-
-        #region Directory Tests
-
-
-        [Theory,
-            MemberData(nameof(ValidPathComponentNames))]
-        public void ValidDirectoryPathExists_ReturnsTrue(string component)
-        {
-            string path = Path.Combine(TestDirectory, component);
-            Directory.CreateDirectory(path);
-            Assert.True(Exists(path));
-        }
-
-        [Fact]
-        public void PathAlreadyExistsAsDirectory()
-        {
-            string path = GetTestFilePath();
-            Directory.CreateDirectory(path);
-
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(path)));
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
-        }
-
-        [Fact]
-        public void DotAsPath_ReturnsTrue()
-        {
-            Assert.True(Exists(Path.Combine(TestDirectory, ".")));
-        }
-
-        [Fact]
-        public void DirectoryGetCurrentDirectoryAsPath_ReturnsTrue()
-        {
-            Assert.True(Exists(Directory.GetCurrentDirectory()));
-        }
-
-        [Fact]
-        public void DotDotAsPath_ReturnsTrue()
-        {
-            Assert.True(Exists(Path.Combine(TestDirectory, GetTestFileName(), "..")));
-        }
-
-        [Fact]
-        public void DirectoryLongerThanMaxLongPath_DoesntThrow()
-        {
-            Assert.All((IOInputs.GetPathsLongerThanMaxLongPath(GetTestFilePath())), (path) =>
-            {
-                Assert.False(Exists(path), path);
-            });
-        }
-
-        [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))]
-        public void DirectorySymLinksMayExistIndependentlyOfTarget()
-        {
-            var path = GetTestFilePath();
-            var linkPath = GetRandomLinkPath();
-
-            Directory.CreateDirectory(path);
-            Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: true));
-
-            // Both the symlink and the target exist
-            Assert.True(Directory.Exists(path), "path should exist");
-            Assert.True(Directory.Exists(linkPath), "linkPath should exist");
-            Assert.False(File.Exists(linkPath));
-
-            // Delete the target.  The symlink should still exist.  On Unix, the symlink will now be
-            // considered a file (since it's broken and we don't know what it'll eventually point to).
-            Directory.Delete(path);
-            Assert.False(Directory.Exists(path), "path should now not exist");
-            if (OperatingSystem.IsWindows())
-            {
-                Assert.True(Directory.Exists(linkPath), "linkPath should still exist as a directory");
-                Assert.False(File.Exists(linkPath), "linkPath should not be a file");
-            }
-            else
-            {
-                Assert.False(Directory.Exists(linkPath), "linkPath should no longer be a directory");
-                Assert.True(File.Exists(linkPath), "linkPath should now be a file");
-            }
-
-            // Now delete the symlink.
-            // On Unix, deleting the symlink should fail, because it's not a directory, it's a file.
-            // On Windows, it should succeed.
-            try
-            {
-                Directory.Delete(linkPath);
-                Assert.True(OperatingSystem.IsWindows(), "Should only succeed on Windows");
-            }
-            catch (IOException)
-            {
-                Assert.False(OperatingSystem.IsWindows(), "Should only fail on Unix");
-                File.Delete(linkPath);
-            }
-
-            Assert.False(Directory.Exists(linkPath), "linkPath should no longer exist as a directory");
-            Assert.False(File.Exists(linkPath), "linkPath should no longer exist as a file");
-        }
-
-        [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))]
-        public void SymlinkToNewDirectory()
-        {
-            string path = GetTestFilePath();
-            Directory.CreateDirectory(path);
-
-            string linkPath = GetRandomLinkPath();
-            Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: true));
-
-            Assert.True(Directory.Exists(path));
-            Assert.True(Directory.Exists(linkPath));
-        }
-
-        #endregion
-
-        #region Platform Specific Common Tests
-        [Theory,
-           MemberData(nameof(WhiteSpace))]
-        [PlatformSpecific(TestPlatforms.Windows)] // alternate data stream
-        public void PathWithAlternateDataStreams_ReturnsFalse(string component)
-        {
-            Assert.False(Exists(component));
-
-        }
-
-        [ConditionalTheory(nameof(ReservedDeviceNamesAreBlocked))] // device names
-        [MemberData(nameof(PathsWithReservedDeviceNames))]
-        [OuterLoop]
-        public void PathWithReservedDeviceNameAsPath_ReturnsFalse(string component)
-        {
-            Assert.False(Exists(component));
-        }
-
-        [ActiveIssue("https://github.com/dotnet/runtimelab/issues/901", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
-        [Theory,
-            MemberData(nameof(UncPathsWithoutShareName))]
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/51371", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
-        public void UncPathWithoutShareNameAsPath_ReturnsFalse(string component)
-        {
-            Assert.False(Exists(component));
-        }
-
-        [Theory,
-          MemberData(nameof(PathsWithComponentLongerThanMaxComponent))]
-        [PlatformSpecific(TestPlatforms.Windows)] // max directory length not fixed on Unix
-        public void DirectoryWithComponentLongerThanMaxComponentAsPath_ReturnsFalse(string component)
-        {
-            Assert.False(Exists(component));
-        }
-        [Fact]
-        [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)]  // Makes call to native code (libc)
-        public void FalseForNonRegularFile()
-        {
-            string fileName = GetTestFilePath();
-            Assert.Equal(0, mkfifo(fileName, 0));
-            Assert.False(Directory.Exists(fileName));
-        }
-        #endregion
-
-        #region Platform Specific File Tests
-
-        [Theory,
-            MemberData(nameof(WhiteSpace))]
-        [PlatformSpecific(TestPlatforms.Windows)] // Unix equivalent tested already in CreateDirectory
-        public void WindowsNonSignificantWhiteSpaceAsPath_ReturnsFalse(string component)
-        {
-            // Checks that errors aren't thrown when calling Exists() on impossible paths
-            Assert.False(Exists(component));
-
-        }
-
-        [Fact]
-        [PlatformSpecific(CaseInsensitivePlatforms)]
-        public void DoesCaseInsensitiveInvariantFileComparions()
-        {
-            FileInfo testFile = new FileInfo(GetTestFilePath());
-            testFile.Create().Dispose();
-            Assert.True(Exists(testFile.FullName));
-            Assert.True(Exists(testFile.FullName.ToUpperInvariant()));
-            Assert.True(Exists(testFile.FullName.ToLowerInvariant()));
-        }
-
-        [Fact]
-        [PlatformSpecific(CaseSensitivePlatforms)]
-        public void DoesCaseSensitiveFileComparisons()
-        {
-            FileInfo testFile = new FileInfo(GetTestFilePath());
-            testFile.Create().Dispose();
-            Assert.True(Exists(testFile.FullName));
-            Assert.False(Exists(testFile.FullName.ToUpperInvariant()));
-            Assert.False(Exists(testFile.FullName.ToLowerInvariant()));
-        }
-
-        [Theory,
-           MemberData(nameof(NonControlWhiteSpace))]
-        [PlatformSpecific(TestPlatforms.Windows)]
-        public void TrailingWhiteSpace_NotTrimmed(string component)
-        {
-            // In .NET Core we don't trim anything other than space (' ')
-            string path = GetTestFilePath() + component;
-            FileInfo testFile = new FileInfo(path);
-            testFile.Create().Dispose();
-
-            Assert.True(Exists(path));
-        }
-
-        [Theory,
-           MemberData(nameof(SimpleWhiteSpace))] //*Just* spaces
-        [PlatformSpecific(TestPlatforms.Windows)] // In Windows, trailing whitespace in a path is trimmed
-        public void TrailingSpace_Trimmed(string component)
-        {
-            FileInfo testFile = new FileInfo(GetTestFilePath());
-            testFile.Create().Dispose();
-
-            // Windows will trim trailing spaces
-            Assert.True(Exists(testFile.FullName + component));
-        }
-        #endregion
-
-        #region Platform Specific Directory Tests
-
-        [ConditionalTheory(nameof(UsingNewNormalization)),
-            MemberData(nameof(ValidPathComponentNames))]
-        [PlatformSpecific(TestPlatforms.Windows)]  // Extended path exists
-        public void ValidExtendedPathExists_ReturnsTrue(string component)
-        {
-            string path = IOInputs.ExtendedPrefix + Path.Combine(TestDirectory, "extended", component);
-            Directory.CreateDirectory(path);
-            Assert.True(Exists(path));
-        }
-
-        [ConditionalFact(nameof(UsingNewNormalization))]
-        [PlatformSpecific(TestPlatforms.Windows)]  // Extended path already exists as file
-        public void ExtendedPathAlreadyExistsAsFile()
-        {
-            string path = IOInputs.ExtendedPrefix + GetTestFilePath();
-            File.Create(path).Dispose();
-
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(path)));
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
-        }
-
-        [ConditionalFact(nameof(UsingNewNormalization))]
-        [PlatformSpecific(TestPlatforms.Windows)]  // Extended path already exists as directory
-        public void ExtendedPathAlreadyExistsAsDirectory()
-        {
-            string path = IOInputs.ExtendedPrefix + GetTestFilePath();
-            Directory.CreateDirectory(path);
-
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(path)));
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
-            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
-        }
-
-        [ConditionalFact(nameof(AreAllLongPathsAvailable))]
-        [PlatformSpecific(TestPlatforms.Windows)]  // Long directory path doesn't throw on Exists
-        public void DirectoryLongerThanMaxDirectoryAsPath_DoesntThrow()
-        {
-            Assert.All((IOInputs.GetPathsLongerThanMaxDirectory(GetTestFilePath())), (path) =>
-            {
-                Assert.False(Exists(path));
-            });
-        }
-
-        [Theory,
-            MemberData(nameof(WhiteSpace))]
-        [PlatformSpecific(TestPlatforms.Windows)] // Unix equivalent tested already in CreateDirectory
-        public void WindowsWhiteSpaceAsPath_ReturnsFalse(string component)
-        {
-            // Checks that errors aren't thrown when calling Exists() on impossible paths
-            Assert.False(Exists(component));
-
-        }
-
-        [Fact]
-        [PlatformSpecific(CaseInsensitivePlatforms)]
-        public void DoesCaseInsensitiveInvariantDirectoryComparisons()
-        {
-            DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
-            Assert.True(Exists(testDir.FullName));
-            Assert.True(Exists(testDir.FullName.ToUpperInvariant()));
-            Assert.True(Exists(testDir.FullName.ToLowerInvariant()));
-        }
-
-        [Fact]
-        [PlatformSpecific(CaseSensitivePlatforms)]
-        public void DoesCaseSensitiveDirectoryComparisons()
-        {
-            DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
-            Assert.True(Exists(testDir.FullName));
-            Assert.False(Exists(testDir.FullName.ToUpperInvariant()));
-            Assert.False(Exists(testDir.FullName.ToLowerInvariant()));
-        }
-
-        [ConditionalTheory(nameof(UsingNewNormalization)),
-            MemberData(nameof(SimpleWhiteSpace))]
-        [PlatformSpecific(TestPlatforms.Windows)] // In Windows, trailing whitespace in a path is trimmed appropriately
-        public void TrailingWhitespaceExistence_SimpleWhiteSpace(string component)
-        {
-            // This test relies on \\?\ support
-
-            DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
-
-            string path = GetTestFilePath(memberName: "Extended") + component;
-            testDir = Directory.CreateDirectory(IOInputs.ExtendedPrefix + path);
-            Assert.False(Exists(path), path);
-            Assert.True(Exists(testDir.FullName));
-        }
-
-        [Theory,
-            MemberData(nameof(NonControlWhiteSpace))]
-        [PlatformSpecific(TestPlatforms.Windows)]
-        public void NonControlWhiteSpaceExists(string component)
-        {
-            DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath() + component);
-
-            string path = testDir.FullName;
-            Assert.True(Exists(path), "directory with non control whitespace should exist");
-        }
-
-        [Theory,
-            MemberData(nameof(SimpleWhiteSpace))] // *Just spaces*
-        [PlatformSpecific(TestPlatforms.Windows)]
-        public void TrailingSpaceExists(string component)
-        {
-            // Windows trims spaces
-            string path = GetTestFilePath();
-            Directory.CreateDirectory(path + component);
-            Assert.True(Exists(path), "can find without space");
-            Assert.True(Exists(path + component), "can find with space");
-        }
-
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // max directory length not fixed on Unix
-        public void DirectoryEqualToMaxDirectory_ReturnsTrue()
-        {
-            // Creates directories up to the maximum directory length all at once
-            DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
-            string path = IOServices.GetPath(testDir.FullName, IOInputs.MaxDirectory);
-            Directory.CreateDirectory(path);
-            Assert.True(Exists(path));
-        }
-
-        [Fact]
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/14378")]
-        [PlatformSpecific(TestPlatforms.Windows)] // drive labels
-        public void NotReadyDriveAsPath_ReturnsFalse()
-        {
-            var drive = IOServices.GetNotReadyDrive();
-            if (drive == null)
-            {
-                Console.WriteLine("Skipping test. Unable to find a not-ready drive, such as CD-Rom with no disc inserted.");
-                return;
-            }
-
-            bool result = Exists(drive);
-
-            Assert.False(result);
-        }
-
-        [Fact]
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/14378")]
-        [PlatformSpecific(TestPlatforms.Windows)] // drive labels
-        public void SubdirectoryOnNotReadyDriveAsPath_ReturnsFalse()
-        {
-            var drive = IOServices.GetNotReadyDrive();
-            if (drive == null)
-            {
-                Console.WriteLine("Skipping test. Unable to find a not-ready drive, such as CD-Rom with no disc inserted.");
-                return;
-            }
-
-            bool result = Exists(Path.Combine(drive, "Subdirectory"));
-
-            Assert.False(result);
-        }
-
-        // Not all drives may be accessible (locked, no rights, etc.), and as such would return false.
-        // eg. Create a new volume, bitlocker it, and lock it. This new volume is no longer accessible
-        // and any attempt to access this drive will return false.
-        // We just care that we can access an accessible drive directly, we don't care which one.
-        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInAppContainer))] // Can't read root in appcontainer
-        [PlatformSpecific(TestPlatforms.Windows)] // drive labels
-        public void DriveAsPath()
-        {
-            Assert.False(Exists(IOServices.GetNonExistentDrive()));
-            Assert.Contains(IOServices.GetReadyDrives(), drive => Exists(drive));
-        }
-
-        [ConditionalFact(nameof(UsingNewNormalization))]
-        [PlatformSpecific(TestPlatforms.Windows)] // drive labels
-        public void ExtendedDriveAsPath()
-        {
-            Assert.False(Exists(IOInputs.ExtendedPrefix + IOServices.GetNonExistentDrive()));
-
-            if (PlatformDetection.IsNotInAppContainer)
-                Assert.Contains(IOServices.GetReadyDrives(), drive => Exists(IOInputs.ExtendedPrefix + drive));
-        }
-
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // drive labels
-        public void SubdirectoryOnNonExistentDriveAsPath_ReturnsFalse()
-        {
-            Assert.False(Exists(Path.Combine(IOServices.GetNonExistentDrive(), "nonexistentsubdir")));
-        }
-
-        #endregion
-    }
-}
diff --git a/src/libraries/System.IO.FileSystem/tests/Path/Exists_Directory.cs b/src/libraries/System.IO.FileSystem/tests/Path/Exists_Directory.cs
new file mode 100644
index 00000000000000..282951a25e89af
--- /dev/null
+++ b/src/libraries/System.IO.FileSystem/tests/Path/Exists_Directory.cs
@@ -0,0 +1,43 @@
+﻿// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.IO.Tests
+{
+    public class PathDirectory_Exists : Directory_Exists
+    {
+        public override bool Exists(string path) => Path.Exists(path);
+
+        #region Universal
+
+        [Fact]
+        public void PathAlreadyExistsAsFile()
+        {
+            string path = GetTestFilePath();
+            File.Create(path).Dispose();
+
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(path)));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
+        }
+
+        #endregion
+
+        #region PlatformSpecific
+
+        [ConditionalFact(nameof(UsingNewNormalization))]
+        [PlatformSpecific(TestPlatforms.Windows)]  // Extended path already exists as file
+        public void ExtendedPathAlreadyExistsAsFile()
+        {
+            string path = IOInputs.ExtendedPrefix + GetTestFilePath();
+            File.Create(path).Dispose();
+
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(path)));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
+        }
+
+        #endregion
+    }
+}
diff --git a/src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs b/src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs
new file mode 100644
index 00000000000000..041ce418b841b2
--- /dev/null
+++ b/src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs
@@ -0,0 +1,49 @@
+﻿// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.IO.Tests
+{
+    public class PathFile_Exists : File_Exists
+    {
+        public override bool Exists(string path) => Path.Exists(path);
+
+        #region Universal
+
+        [Fact]
+        public void PathEndsInTrailingSlash_AndExists()
+        {
+            string path = GetTestFilePath();
+            File.Create(path).Dispose();
+            Assert.True(Exists(path + Path.DirectorySeparatorChar));
+        }
+
+        [Fact]
+        public void PathAlreadyExistsAsDirectory()
+        {
+            string path = GetTestFilePath();
+            Directory.CreateDirectory(path);
+
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(path)));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
+            Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
+        }
+
+
+        #endregion
+
+        #region PlatformSpecific
+
+        [Fact]
+        [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)]  // Uses P/Invokes
+        public void FalseForNonRegularFile()
+        {
+            string fileName = GetTestFilePath();
+            Assert.Equal(0, mkfifo(fileName, 0));
+            Assert.True(Exists(fileName));
+        }
+
+        #endregion
+    }
+}
diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
index 78b42f58ba60c8..967338535cd01f 100644
--- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
+++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
@@ -58,7 +58,8 @@
     <Compile Include="Enumeration\SymbolicLinksTests.cs" />
     <Compile Include="LargeFileTests.cs" />
     <Compile Include="PathInternalTests.cs" />
-    <Compile Include="Path\Exists.cs" />
+    <Compile Include="Path\Exists_Directory.cs" />
+    <Compile Include="Path\Exists_File.cs" />
     <Compile Include="RandomAccess\Base.cs" />
     <Compile Include="RandomAccess\GetLength.cs" />
     <Compile Include="RandomAccess\Read.cs" />
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
index 4905c2b153646c..8730422dd5e36c 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
@@ -18,18 +18,13 @@ public static bool Exists([NotNullWhen(true)] string? path)
         {
             try
             {
-                if (path == null)
-                {
-                    return false;
-                }
-
-                if (path.Length == 0)
+               if (string.IsNullOrEmpty(path))
                 {
                     return false;
                 }
 
                 ReadOnlySpan<char> fullPath = GetFullPath(path).AsSpan();
-                return Interop.Sys.LStat(fullPath, out Interop.Sys.FileStatus _) > 0;
+                return Interop.Sys.LStat(fullPath, out fileinfo) == 0;
             }
 
             catch (ArgumentException) { }
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
index cd6115577f66b2..dff75bca8c0ec1 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
@@ -27,24 +27,21 @@ public static partial class Path
             (char)31
         };
 
-        //Checks if the given path is available for use.
+        // Checks if the given path is available for use.
         public static bool Exists([NotNullWhen(true)] string? path)
         {
             try
             {
-                if (path == null)
-                {
-                    return false;
-                }
-
-                if (path.Length == 0)
+                if (string.IsNullOrEmpty(path))
                 {
                     return false;
                 }
 
                 string fullPath = GetFullPath(path);
                 Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = default;
-                return FileSystem.FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true) == 0;
+                int errorCode = FileSystem.FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true);
+
+                return (errorCode == 0) && (data.dwFileAttributes != -1);
             }
 
             catch (ArgumentException) { }

From f116c980dd656b8dd858778bfbeed9f034eaf401 Mon Sep 17 00:00:00 2001
From: Tarun <gtarun047@gmail.com>
Date: Thu, 27 Jan 2022 21:15:51 +0530
Subject: [PATCH 04/11] Fix Unix Exists

---
 .../src/System/IO/Path.Unix.cs                | 20 ++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
index 8730422dd5e36c..6734a211238057 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
@@ -9,27 +9,33 @@ namespace System.IO
 {
     public static partial class Path
     {
-        public static char[] GetInvalidFileNameChars() => new char[] { '\0', '/' };
+        public static char[] GetInvalidFileNameChars() => new char[] {'\0', '/'};
 
-        public static char[] GetInvalidPathChars() => new char[] { '\0' };
+        public static char[] GetInvalidPathChars() => new char[] {'\0'};
 
         // Checks if the given path is available for use.
         public static bool Exists([NotNullWhen(true)] string? path)
         {
             try
             {
-               if (string.IsNullOrEmpty(path))
+                if (string.IsNullOrEmpty(path))
                 {
                     return false;
                 }
 
                 ReadOnlySpan<char> fullPath = GetFullPath(path).AsSpan();
-                return Interop.Sys.LStat(fullPath, out fileinfo) == 0;
+                return Interop.Sys.LStat(fullPath, out Interop.Sys.FileStatus _) == 0;
             }
 
-            catch (ArgumentException) { }
-            catch (IOException) { }
-            catch (UnauthorizedAccessException) { }
+            catch (ArgumentException)
+            {
+            }
+            catch (IOException)
+            {
+            }
+            catch (UnauthorizedAccessException)
+            {
+            }
 
             return false;
         }

From 71d41b912ec6e5ac9cb7be5c78254db713c1ad69 Mon Sep 17 00:00:00 2001
From: Tarun <gtarun047@gmail.com>
Date: Fri, 28 Jan 2022 12:10:57 +0530
Subject: [PATCH 05/11] Fix Failing Tests

---
 .../System.IO.FileSystem/tests/File/Exists.cs | 35 ++++++++++---------
 .../tests/Path/Exists_File.cs                 |  8 -----
 2 files changed, 18 insertions(+), 25 deletions(-)

diff --git a/src/libraries/System.IO.FileSystem/tests/File/Exists.cs b/src/libraries/System.IO.FileSystem/tests/File/Exists.cs
index d43497e43b1a79..6487cda60eebcf 100644
--- a/src/libraries/System.IO.FileSystem/tests/File/Exists.cs
+++ b/src/libraries/System.IO.FileSystem/tests/File/Exists.cs
@@ -79,6 +79,24 @@ public void PathEndsInAltTrailingSlash_Windows()
             string path = GetTestFilePath() + Path.DirectorySeparatorChar;
             Assert.False(Exists(path));
         }
+
+        [Fact]
+        public void PathEndsInTrailingSlash_AndExists()
+        {
+            string path = GetTestFilePath();
+            File.Create(path).Dispose();
+            Assert.False(Exists(path + Path.DirectorySeparatorChar));
+        }
+
+        [Fact]
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public void PathEndsInAltTrailingSlash_AndExists_Windows()
+        {
+            string path = GetTestFilePath();
+            File.Create(path).Dispose();
+            Assert.False(Exists(path + Path.DirectorySeparatorChar));
+        }
+
         [Fact]
         [ActiveIssue("https://github.com/dotnet/runtime/issues/51371", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
         public void DirectoryLongerThanMaxDirectoryAsPath_DoesntThrow()
@@ -230,14 +248,6 @@ public void DotAsPathReturnsFalse()
             Assert.False(File.Exists(".."));
         }
 
-        [Fact]
-        public void PathEndsInTrailingSlash_AndExists()
-        {
-            string path = GetTestFilePath();
-            File.Create(path).Dispose();
-            Assert.False(File.Exists(path + Path.DirectorySeparatorChar));
-        }
-
         [Fact]
         public void PathAlreadyExistsAsDirectory()
         {
@@ -262,15 +272,6 @@ public void FalseForNonRegularFile()
             Assert.True(File.Exists(fileName));
         }
 
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)]
-        public void PathEndsInAltTrailingSlash_AndExists_Windows()
-        {
-            string path = GetTestFilePath();
-            File.Create(path).Dispose();
-            Assert.False(File.Exists(path + Path.DirectorySeparatorChar));
-        }
-
         #endregion
     }
 }
diff --git a/src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs b/src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs
index 041ce418b841b2..0a8864e4025a60 100644
--- a/src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs
+++ b/src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs
@@ -11,14 +11,6 @@ public class PathFile_Exists : File_Exists
 
         #region Universal
 
-        [Fact]
-        public void PathEndsInTrailingSlash_AndExists()
-        {
-            string path = GetTestFilePath();
-            File.Create(path).Dispose();
-            Assert.True(Exists(path + Path.DirectorySeparatorChar));
-        }
-
         [Fact]
         public void PathAlreadyExistsAsDirectory()
         {

From 4d93105f88bc0a39055131bb6e5cf0ca1cf0cbee Mon Sep 17 00:00:00 2001
From: Tarun047 <gtarun047@gmail.com>
Date: Fri, 28 Jan 2022 13:55:04 +0530
Subject: [PATCH 06/11] Fix Tests on Windows

---
 .../src/System/IO/Path.Windows.cs                     | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
index dff75bca8c0ec1..c8a25871fb6203 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
@@ -40,8 +40,17 @@ public static bool Exists([NotNullWhen(true)] string? path)
                 string fullPath = GetFullPath(path);
                 Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = default;
                 int errorCode = FileSystem.FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true);
+                bool result = (errorCode == 0) && (data.dwFileAttributes != -1);
 
-                return (errorCode == 0) && (data.dwFileAttributes != -1);
+                if (PathInternal.IsDirectorySeparator(path[path.Length - 1]))
+                {
+                    // We want to make sure that if the path ends in a trailing slash, it's truly a directory
+                    // because FillAttributeInfo syscall removes any trailing slashes and may give false positives
+                    // for existing files.
+                    result = result && (data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0;
+                }
+
+                return result;
             }
 
             catch (ArgumentException) { }

From e64094f17b16b078f2813d493234cd7a99152b25 Mon Sep 17 00:00:00 2001
From: Tarun <gtarun047@gmail.com>
Date: Fri, 28 Jan 2022 20:03:37 +0530
Subject: [PATCH 07/11] Fix Code on Unix Side

---
 .../tests/Path/Exists_Directory.cs            |  8 ----
 .../tests/Path/Exists_File.cs                 | 11 +-----
 .../src/System/IO/Path.Unix.cs                | 25 +-----------
 .../src/System/IO/Path.Windows.cs             | 38 ++++++-------------
 .../src/System/IO/Path.cs                     | 33 ++++++++++++++++
 5 files changed, 47 insertions(+), 68 deletions(-)

diff --git a/src/libraries/System.IO.FileSystem/tests/Path/Exists_Directory.cs b/src/libraries/System.IO.FileSystem/tests/Path/Exists_Directory.cs
index 282951a25e89af..9c4831d585771e 100644
--- a/src/libraries/System.IO.FileSystem/tests/Path/Exists_Directory.cs
+++ b/src/libraries/System.IO.FileSystem/tests/Path/Exists_Directory.cs
@@ -9,8 +9,6 @@ public class PathDirectory_Exists : Directory_Exists
     {
         public override bool Exists(string path) => Path.Exists(path);
 
-        #region Universal
-
         [Fact]
         public void PathAlreadyExistsAsFile()
         {
@@ -22,10 +20,6 @@ public void PathAlreadyExistsAsFile()
             Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
         }
 
-        #endregion
-
-        #region PlatformSpecific
-
         [ConditionalFact(nameof(UsingNewNormalization))]
         [PlatformSpecific(TestPlatforms.Windows)]  // Extended path already exists as file
         public void ExtendedPathAlreadyExistsAsFile()
@@ -37,7 +31,5 @@ public void ExtendedPathAlreadyExistsAsFile()
             Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.RemoveTrailingSlash(path))));
             Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
         }
-
-        #endregion
     }
 }
diff --git a/src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs b/src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs
index 0a8864e4025a60..64af8e84c009d2 100644
--- a/src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs
+++ b/src/libraries/System.IO.FileSystem/tests/Path/Exists_File.cs
@@ -9,8 +9,6 @@ public class PathFile_Exists : File_Exists
     {
         public override bool Exists(string path) => Path.Exists(path);
 
-        #region Universal
-
         [Fact]
         public void PathAlreadyExistsAsDirectory()
         {
@@ -22,20 +20,13 @@ public void PathAlreadyExistsAsDirectory()
             Assert.True(Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
         }
 
-
-        #endregion
-
-        #region PlatformSpecific
-
         [Fact]
         [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)]  // Uses P/Invokes
-        public void FalseForNonRegularFile()
+        public void TrueForNonRegularFile()
         {
             string fileName = GetTestFilePath();
             Assert.Equal(0, mkfifo(fileName, 0));
             Assert.True(Exists(fileName));
         }
-
-        #endregion
     }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
index 6734a211238057..3e17174eb4f875 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
@@ -14,30 +14,9 @@ public static partial class Path
         public static char[] GetInvalidPathChars() => new char[] {'\0'};
 
         // Checks if the given path is available for use.
-        public static bool Exists([NotNullWhen(true)] string? path)
+        private static bool ExistsCore(string fullPath)
         {
-            try
-            {
-                if (string.IsNullOrEmpty(path))
-                {
-                    return false;
-                }
-
-                ReadOnlySpan<char> fullPath = GetFullPath(path).AsSpan();
-                return Interop.Sys.LStat(fullPath, out Interop.Sys.FileStatus _) == 0;
-            }
-
-            catch (ArgumentException)
-            {
-            }
-            catch (IOException)
-            {
-            }
-            catch (UnauthorizedAccessException)
-            {
-            }
-
-            return false;
+            return Interop.Sys.LStat(fullPath, out Interop.Sys.FileStatus _) == Interop.Errors.ERROR_SUCCESS;
         }
 
         // Expands the given path to a fully qualified path.
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
index c8a25871fb6203..265d6791f1de8d 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
@@ -27,37 +27,21 @@ public static partial class Path
             (char)31
         };
 
-        // Checks if the given path is available for use.
-        public static bool Exists([NotNullWhen(true)] string? path)
+        private static bool ExistsCore(string fullPath)
         {
-            try
-            {
-                if (string.IsNullOrEmpty(path))
-                {
-                    return false;
-                }
+            Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = default;
+            int errorCode = FileSystem.FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true);
+            bool result = (errorCode == Interop.Errors.ERROR_SUCCESS) && (data.dwFileAttributes != -1);
 
-                string fullPath = GetFullPath(path);
-                Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = default;
-                int errorCode = FileSystem.FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true);
-                bool result = (errorCode == 0) && (data.dwFileAttributes != -1);
-
-                if (PathInternal.IsDirectorySeparator(path[path.Length - 1]))
-                {
-                    // We want to make sure that if the path ends in a trailing slash, it's truly a directory
-                    // because FillAttributeInfo syscall removes any trailing slashes and may give false positives
-                    // for existing files.
-                    result = result && (data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0;
-                }
-
-                return result;
+            if (PathInternal.IsDirectorySeparator(path[path.Length - 1]))
+            {
+                // We want to make sure that if the path ends in a trailing slash, it's truly a directory
+                // because FillAttributeInfo syscall removes any trailing slashes and may give false positives
+                // for existing files.
+                result = result && (data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0;
             }
 
-            catch (ArgumentException) { }
-            catch (IOException) { }
-            catch (UnauthorizedAccessException) { }
-
-            return false;
+            return result;
         }
 
         // Expands the given path to a fully qualified path.
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
index b2887383294eb5..26e3d2f2eb7638 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
@@ -75,6 +75,39 @@ public static partial class Path
                 string.Concat(subpath, ".", extension);
         }
 
+        /// <summary>
+        ///  Determines whether a given path exists on the filesystem.
+        /// true if the caller has the required permissions and the path exists on the filesystem; otherwise, false.
+        /// This method also returns false if path is null, an invalid path, or a zero-length string.
+        /// If the caller does not have sufficient permissions to read the specified path,
+        /// no exception is thrown and the method returns false regardless of the existence of path.
+        /// </summary>
+        /// <remarks>
+        /// It would also return true for existing, non-regular files like pipes unlike File.Exists.
+        /// Logically this is equivalent to File.Exists(path) || Directory.Exists(path).
+        /// </remarks>
+        /// <param name="path">The path to check</param>
+        /// <returns> Boolean </returns>
+        public static bool Exists([NotNullWhen(true)] string? path)
+        {
+            if (string.IsNullOrEmpty(path))
+            {
+                return false;
+            }
+
+            string? fullPath;
+            try
+            {
+                fullPath = GetFullPath(path);
+            }
+            catch (Exception ex) when (ex is ArgumentException or IOException or UnauthorizedAccessException)
+            {
+                return false;
+            }
+
+            return ExistsCore(fullPath);
+        }
+
         /// <summary>
         /// Returns the directory portion of a file path. This method effectively
         /// removes the last segment of the given file path, i.e. it returns a

From 57f96e0e911bd6e590a31ce5ab12016505c4e060 Mon Sep 17 00:00:00 2001
From: Tarun047 <gtarun047@gmail.com>
Date: Fri, 28 Jan 2022 20:51:33 +0530
Subject: [PATCH 08/11] Fix Tests on Windows

---
 .../System.Private.CoreLib/src/System/IO/Path.Windows.cs        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
index 265d6791f1de8d..2c8a635c05a9c5 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs
@@ -33,7 +33,7 @@ private static bool ExistsCore(string fullPath)
             int errorCode = FileSystem.FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true);
             bool result = (errorCode == Interop.Errors.ERROR_SUCCESS) && (data.dwFileAttributes != -1);
 
-            if (PathInternal.IsDirectorySeparator(path[path.Length - 1]))
+            if (PathInternal.IsDirectorySeparator(fullPath[fullPath.Length - 1]))
             {
                 // We want to make sure that if the path ends in a trailing slash, it's truly a directory
                 // because FillAttributeInfo syscall removes any trailing slashes and may give false positives

From 8d915c3b6eb4ee472fdc4c6246a2a7bee65142ca Mon Sep 17 00:00:00 2001
From: Tarun <gtarun047@gmail.com>
Date: Fri, 28 Jan 2022 21:25:37 +0530
Subject: [PATCH 09/11] Minor Syntax Fixes

---
 .../System.IO.FileSystem/tests/Directory/Exists.cs        | 7 -------
 src/libraries/System.IO.FileSystem/tests/File/Exists.cs   | 8 --------
 .../System.Private.CoreLib/src/System/IO/Path.Unix.cs     | 4 ++--
 3 files changed, 2 insertions(+), 17 deletions(-)

diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs b/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs
index 434bc1dc8390fc..2628e7b27e1369 100644
--- a/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs
+++ b/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs
@@ -371,7 +371,6 @@ public void SubdirectoryOnNonExistentDriveAsPath_ReturnsFalse()
 
     public class Directory_ExistsAsFile : FileSystemTest
     {
-        #region Universal
 
         [Fact]
         public void PathAlreadyExistsAsFile()
@@ -384,10 +383,6 @@ public void PathAlreadyExistsAsFile()
             Assert.False(Directory.Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
         }
 
-        #endregion
-
-        #region PlatformSpecific
-
         [ConditionalFact(nameof(UsingNewNormalization))]
         [PlatformSpecific(TestPlatforms.Windows)]  // Extended path already exists as file
         public void ExtendedPathAlreadyExistsAsFile()
@@ -408,7 +403,5 @@ public void FalseForNonRegularFile()
             Assert.Equal(0, mkfifo(fileName, 0));
             Assert.False(Directory.Exists(fileName));
         }
-
-        #endregion
     }
 }
diff --git a/src/libraries/System.IO.FileSystem/tests/File/Exists.cs b/src/libraries/System.IO.FileSystem/tests/File/Exists.cs
index 95db6573c03ae7..882006efed2a93 100644
--- a/src/libraries/System.IO.FileSystem/tests/File/Exists.cs
+++ b/src/libraries/System.IO.FileSystem/tests/File/Exists.cs
@@ -237,8 +237,6 @@ public void DirectoryWithComponentLongerThanMaxComponentAsPath_ReturnsFalse(stri
 
     public class File_ExistsAsDirectory : FileSystemTest
     {
-        #region Universal
-
         [Fact]
         public void DotAsPathReturnsFalse()
         {
@@ -257,10 +255,6 @@ public void PathAlreadyExistsAsDirectory()
             Assert.False(File.Exists(IOServices.RemoveTrailingSlash(IOServices.AddTrailingSlashIfNeeded(path))));
         }
 
-        #endregion
-
-        #region PlatformSpecific
-
         [Fact]
         [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)]  // Uses P/Invokes
         public void FalseForNonRegularFile()
@@ -269,7 +263,5 @@ public void FalseForNonRegularFile()
             Assert.Equal(0, mkfifo(fileName, 0));
             Assert.True(File.Exists(fileName));
         }
-
-        #endregion
     }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
index 3e17174eb4f875..dde35dca12aaa8 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
@@ -9,9 +9,9 @@ namespace System.IO
 {
     public static partial class Path
     {
-        public static char[] GetInvalidFileNameChars() => new char[] {'\0', '/'};
+        public static char[] GetInvalidFileNameChars() => new char[] { '\0', '/' };
 
-        public static char[] GetInvalidPathChars() => new char[] {'\0'};
+        public static char[] GetInvalidPathChars() => new char[] { '\0' };
 
         // Checks if the given path is available for use.
         private static bool ExistsCore(string fullPath)

From 52f281be49feed8eaaec02603318846230c950c8 Mon Sep 17 00:00:00 2001
From: Tarun047 <32017154+Tarun047@users.noreply.github.com>
Date: Fri, 28 Jan 2022 22:08:27 +0530
Subject: [PATCH 10/11] Apply suggestions from code review

Co-authored-by: Adam Sitnik <adam.sitnik@gmail.com>
---
 .../tests/Directory/Exists.cs                  |  1 -
 .../src/System/IO/Path.cs                      | 18 ++++++++++--------
 2 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs b/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs
index 2628e7b27e1369..72a5a3cae666ff 100644
--- a/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs
+++ b/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs
@@ -371,7 +371,6 @@ public void SubdirectoryOnNonExistentDriveAsPath_ReturnsFalse()
 
     public class Directory_ExistsAsFile : FileSystemTest
     {
-
         [Fact]
         public void PathAlreadyExistsAsFile()
         {
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
index 26e3d2f2eb7638..0595cabe2579aa 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
@@ -76,18 +76,20 @@ public static partial class Path
         }
 
         /// <summary>
-        ///  Determines whether a given path exists on the filesystem.
-        /// true if the caller has the required permissions and the path exists on the filesystem; otherwise, false.
-        /// This method also returns false if path is null, an invalid path, or a zero-length string.
-        /// If the caller does not have sufficient permissions to read the specified path,
-        /// no exception is thrown and the method returns false regardless of the existence of path.
+        /// Determines whether the specified file or directory exists.
         /// </summary>
         /// <remarks>
-        /// It would also return true for existing, non-regular files like pipes unlike File.Exists.
-        /// Logically this is equivalent to File.Exists(path) || Directory.Exists(path).
+        /// Unlike <see cref="File.Exists(string?)"/> it returns true for existing, non-regular files like pipes.
+        /// If the path targets an existing link, but the target of the link does not exist, it returns true.
         /// </remarks>
         /// <param name="path">The path to check</param>
-        /// <returns> Boolean </returns>
+        /// <returns>
+        /// <see langword="true" /> if the caller has the required permissions and <paramref name="path" /> contains
+        /// the name of an existing file or directory; otherwise, <see langword="false" />.
+        /// This method also returns <see langword="false" /> if <paramref name="path" /> is <see langword="null" />,
+        /// an invalid path, or a zero-length string. If the caller does not have sufficient permissions to read the specified path,
+        /// no exception is thrown and the method returns <see langword="false" /> regardless of the existence of <paramref name="path" />.
+        /// </returns>
         public static bool Exists([NotNullWhen(true)] string? path)
         {
             if (string.IsNullOrEmpty(path))

From c8e3fdd806de6a89d9a6c57f33561eb2fff960e3 Mon Sep 17 00:00:00 2001
From: Tarun <gtarun047@gmail.com>
Date: Sat, 29 Jan 2022 08:16:13 +0530
Subject: [PATCH 11/11] Fix Tests on WASM Browser for Unix

---
 .../src/System/IO/Path.Unix.cs                       | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
index dde35dca12aaa8..b395d33594a2fc 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs
@@ -16,7 +16,17 @@ public static partial class Path
         // Checks if the given path is available for use.
         private static bool ExistsCore(string fullPath)
         {
-            return Interop.Sys.LStat(fullPath, out Interop.Sys.FileStatus _) == Interop.Errors.ERROR_SUCCESS;
+            bool result = Interop.Sys.LStat(fullPath, out Interop.Sys.FileStatus fileInfo) == Interop.Errors.ERROR_SUCCESS;
+            if (PathInternal.IsDirectorySeparator(fullPath[fullPath.Length - 1]))
+            {
+                // If the path ends with trailing slash, we want to make sure it's a directory.
+                // Although Lstat returns the correct result on desktop platforms,
+                // on browser WASM, it seems to strip trailing slash, leading to false positives for files.
+                // This check prevents it from doing so.
+                result = result && (fileInfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR;
+            }
+
+            return result;
         }
 
         // Expands the given path to a fully qualified path.
