From 6bc2b55fdf99bf70cf11bbc2a37ffa20aa9881d5 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 29 Mar 2019 13:22:52 -0700 Subject: [PATCH 1/8] return correct casing of filesystem path during normalization --- .../namespaces/FileSystemProvider.cs | 41 ++++++++++++++++++- .../Set-Location.Tests.ps1 | 6 +++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index b62454ac5ce..733b12eb03e 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -98,7 +98,46 @@ public FileSystemProvider() /// private static string NormalizePath(string path) { - return path.Replace(StringLiterals.AlternatePathSeparator, StringLiterals.DefaultPathSeparator); + return GetCorrectCasedPath(path.Replace(StringLiterals.AlternatePathSeparator, StringLiterals.DefaultPathSeparator)); + } + + /// + /// Get the correct casing for a path. This method assumes it's being called by NormalizePath() + /// so that the path is already normalized. + /// + /// + /// The path to retrieve. + /// + /// + /// The path with accurate casing if item exists, otherwise it returns path that was passed in. + /// + private static string GetCorrectCasedPath(string path) + { + string exactPath = string.Empty; + if (File.Exists(path) || Directory.Exists(path)) + { + foreach (string item in path.Split(StringLiterals.DefaultPathSeparator)) + { + if (string.IsNullOrEmpty(exactPath)) + { + exactPath = item + StringLiterals.DefaultPathSeparator; + } + else if (string.IsNullOrEmpty(item)) + { + // This handles the trailing slash case + continue; + } + else + { + exactPath = Directory.GetFileSystemEntries(exactPath, item).First(); + } + } + return exactPath; + } + else + { + return path; + } } /// diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 index 5824419f3b1..c401003c2b4 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 @@ -70,6 +70,12 @@ Describe "Set-Location" -Tags "CI" { Should -Throw -ErrorId "PathNotFound,Microsoft.PowerShell.Commands.SetLocationCommand" } + It "Should use actual casing of folder on case-insensitive filesystem" -Skip:($IsLinux) { + $testPath = New-Item -ItemType Directory -Path testdrive:/teST + Set-Location $testPath.FullName.ToUpper() + $(Get-Location).Path | Should -BeExactly $testPath.FullName + } + Context 'Set-Location with no arguments' { It 'Should go to $env:HOME when Set-Location run with no arguments from FileSystem provider' { From bc4b1085d961f4351bae49f8744b59f50558a71d Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Fri, 29 Mar 2019 22:48:11 -0700 Subject: [PATCH 2/8] handle UNC case --- .../namespaces/FileSystemProvider.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 733b12eb03e..1114667683a 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -116,10 +116,25 @@ private static string GetCorrectCasedPath(string path) string exactPath = string.Empty; if (File.Exists(path) || Directory.Exists(path)) { + int itemsToSkip = 0; + if (Utils.PathIsUnc(path)) + { + // With the Split method, a UNC path like \\server\share, we need to skip + // trying to enumerate the server and share, so skip the first two empty + // strings, then server, and finally share name. + itemsToSkip = 4; + } + foreach (string item in path.Split(StringLiterals.DefaultPathSeparator)) { - if (string.IsNullOrEmpty(exactPath)) + if (itemsToSkip-- > 0) { + exactPath += item + StringLiterals.DefaultPathSeparator; + continue; + } + else if (string.IsNullOrEmpty(exactPath)) + { + // This handles the drive letter or / root path start exactPath = item + StringLiterals.DefaultPathSeparator; } else if (string.IsNullOrEmpty(item)) @@ -132,6 +147,7 @@ private static string GetCorrectCasedPath(string path) exactPath = Directory.GetFileSystemEntries(exactPath, item).First(); } } + return exactPath; } else From a3f80e7e9b7f79fc38c28a1289ecc53693345bcd Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Sat, 30 Mar 2019 07:10:40 -0700 Subject: [PATCH 3/8] handle case to add trailing separator back --- .../namespaces/FileSystemProvider.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 1114667683a..bae300d5707 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -148,6 +148,11 @@ private static string GetCorrectCasedPath(string path) } } + if (path.EndsWith(StringLiterals.DefaultPathSeparator)) + { + return exactPath + StringLiterals.DefaultPathSeparator; + } + return exactPath; } else From f0264d0147b76649fb05d0b404aeec4d2bc84208 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Sat, 30 Mar 2019 08:40:32 -0700 Subject: [PATCH 4/8] add support for NTFS streams syntax --- .../namespaces/FileSystemProvider.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index bae300d5707..8454a8c1e8a 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -142,6 +142,14 @@ private static string GetCorrectCasedPath(string path) // This handles the trailing slash case continue; } +#if !UNIX + else if (item.Contains(":")) + { + // This handles the NTFS stream name + var streamName = item.Split(":"); + exactPath = Directory.GetFileSystemEntries(exactPath, streamName[0]).First() + ":" + streamName[1]; + } +#endif else { exactPath = Directory.GetFileSystemEntries(exactPath, item).First(); From 882e94f1cb597459745a2de5bea84b3055387f69 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Sat, 30 Mar 2019 09:40:14 -0700 Subject: [PATCH 5/8] add case-sensitive test --- .../Set-Location.Tests.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 index c401003c2b4..10a276b32b6 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 @@ -76,6 +76,18 @@ Describe "Set-Location" -Tags "CI" { $(Get-Location).Path | Should -BeExactly $testPath.FullName } + It "Should use actual casing of folder on case-sensitive filesystem: " -Skip:(!$IsLinux) + { + $dir = "teST" + $testPathLower = New-Item -ItemType Directory -Path (Join-Path $TestDrive $dir.ToLower()) + $testPathUpper = New-Item -ItemType Directory -Path (Join-Path $TestDrive $dir.ToUpper()) + Set-Location $testPathLower.FullName + $(Get-Location).Path | Should -BeExactly $testPathLower.FullName + Set-Location $testPathUpper.FullName + $(Get-Location).Path | Should -BeExactly $testPathUpper.FullName + { Set-Locaiton (Join-Path $TestDrive $dir) } | Should -Throw -ErrorId "PathNotFound,Microsoft.PowerShell.Commands.SetLocationCommand" + } + Context 'Set-Location with no arguments' { It 'Should go to $env:HOME when Set-Location run with no arguments from FileSystem provider' { From 9e7fe9a9d19e407027cf5ee50132ea259f9f520f Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Sun, 31 Mar 2019 12:06:38 -0700 Subject: [PATCH 6/8] only apply correct casing to directories --- .../namespaces/FileSystemProvider.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 8454a8c1e8a..83f2c40aa92 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -114,7 +114,9 @@ private static string NormalizePath(string path) private static string GetCorrectCasedPath(string path) { string exactPath = string.Empty; - if (File.Exists(path) || Directory.Exists(path)) + // Only apply to directories where there are issues with some tools if the casing + // doesn't match the source like git + if (Directory.Exists(path)) { int itemsToSkip = 0; if (Utils.PathIsUnc(path)) @@ -142,14 +144,6 @@ private static string GetCorrectCasedPath(string path) // This handles the trailing slash case continue; } -#if !UNIX - else if (item.Contains(":")) - { - // This handles the NTFS stream name - var streamName = item.Split(":"); - exactPath = Directory.GetFileSystemEntries(exactPath, streamName[0]).First() + ":" + streamName[1]; - } -#endif else { exactPath = Directory.GetFileSystemEntries(exactPath, item).First(); From 11f93c5a2b7ae2aa5534fa4b7e178ae5ccb606dc Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Mon, 1 Apr 2019 10:41:03 -0700 Subject: [PATCH 7/8] handle 8.3 path syntax --- .../namespaces/FileSystemProvider.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 83f2c40aa92..9106d66a86f 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -113,11 +113,11 @@ private static string NormalizePath(string path) /// private static string GetCorrectCasedPath(string path) { - string exactPath = string.Empty; // Only apply to directories where there are issues with some tools if the casing // doesn't match the source like git if (Directory.Exists(path)) { + string exactPath = string.Empty; int itemsToSkip = 0; if (Utils.PathIsUnc(path)) { @@ -129,8 +129,9 @@ private static string GetCorrectCasedPath(string path) foreach (string item in path.Split(StringLiterals.DefaultPathSeparator)) { - if (itemsToSkip-- > 0) + if (itemsToSkip-- > 0 || item.Contains('~')) { + // This handles the UNC server and share and 8.3 short path syntax exactPath += item + StringLiterals.DefaultPathSeparator; continue; } From 586084fb6b91d2e5bcfece43c7a8becd782518dc Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Mon, 1 Apr 2019 12:05:45 -0700 Subject: [PATCH 8/8] fix short path processing --- .../namespaces/FileSystemProvider.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 9106d66a86f..54c68653ea9 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -129,7 +129,7 @@ private static string GetCorrectCasedPath(string path) foreach (string item in path.Split(StringLiterals.DefaultPathSeparator)) { - if (itemsToSkip-- > 0 || item.Contains('~')) + if (itemsToSkip-- > 0) { // This handles the UNC server and share and 8.3 short path syntax exactPath += item + StringLiterals.DefaultPathSeparator; @@ -145,6 +145,11 @@ private static string GetCorrectCasedPath(string path) // This handles the trailing slash case continue; } + else if (item.Contains('~')) + { + // This handles short path names + exactPath += StringLiterals.DefaultPathSeparator + item; + } else { exactPath = Directory.GetFileSystemEntries(exactPath, item).First();