From d9fe6138bf0375e107e5bda4e1b5e58001ed49da Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Mon, 18 Jul 2022 12:45:11 -0700 Subject: [PATCH 1/3] Change `Get-ChildItem` to treat trailing slash in path as indicating a directory when used with `-Recurse` --- .../engine/SessionStateContainer.cs | 4 ++-- .../namespaces/FileSystemProvider.cs | 8 ++++++++ .../Get-ChildItem.Tests.ps1 | 11 +++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/SessionStateContainer.cs b/src/System.Management.Automation/engine/SessionStateContainer.cs index cf833a7643b..c786587689a 100644 --- a/src/System.Management.Automation/engine/SessionStateContainer.cs +++ b/src/System.Management.Automation/engine/SessionStateContainer.cs @@ -1324,8 +1324,8 @@ internal void GetChildItems( try { // If we're recursing, do some path fixups to match user - // expectations: - if (recurse) + // expectations only if the last part is a file and not a directory: + if (recurse && !path.EndsWith(Path.DirectorySeparatorChar) && !path.EndsWith(Path.AltDirectorySeparatorChar)) { string childName = GetChildName(path, context); diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 3f8c32a2fed..351ea46afde 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1380,6 +1380,14 @@ private FileSystemInfo GetFileSystemItem(string path, ref bool isContainer, bool bool hidden = attributes.HasFlag(FileAttributes.Hidden); isContainer = attributes.HasFlag(FileAttributes.Directory); + // FileInfo allows for a file path to end in a trailing slash, but the resulting object + // is incomplete. A trailing slash should indicate a directory. So if the path ends in a + // trailing slash and is not a directory, return null + if (!isContainer && path.EndsWith(Path.DirectorySeparatorChar)) + { + return null; + } + FlagsExpression evaluator = null; FlagsExpression switchEvaluator = null; GetChildDynamicParameters fspDynamicParam = DynamicParameters as GetChildDynamicParameters; diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 index 5059928f322..a5d06df0b46 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 @@ -217,6 +217,17 @@ Describe "Get-ChildItem" -Tags "CI" { It "Should list the folder present when path length equal to MAX_PATH" { (Get-ChildItem -Path TestDrive:\$item_I -Recurse -Force).Name.Length | Should -BeGreaterThan 0 } + + It 'Trailing slash for -Path should treat it as a folder only when used with -recurse' { + $foo = New-Item -ItemType Directory -Path TestDrive:\foo + $foo2 = New-Item -ItemType Directory -Path TestDrive:\foo\foo + $bar = New-Item -ItemType File -Path TestDrive:\foo\bar + $bar2 = New-Item -ItemType File -Path TestDrive:\foo\foo\bar + + { Get-ChildItem -Path testdrive:/foo/bar/ -Recurse -ErrorAction Stop } | Should -Throw -ErrorId 'ItemNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand' + $barFiles = Get-ChildItem -Path testdrive:/foo/bar -Recurse + $barFiles.Count | Should -Be 2 + } } Context 'Env: Provider' { From dc853f61e2f2cb3560062fa901e8e253ba39c4ff Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 19 Jul 2022 09:28:51 -0700 Subject: [PATCH 2/3] Update src/System.Management.Automation/engine/SessionStateContainer.cs Co-authored-by: Paul Higinbotham --- .../engine/SessionStateContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/SessionStateContainer.cs b/src/System.Management.Automation/engine/SessionStateContainer.cs index c786587689a..0d6f2a97fa2 100644 --- a/src/System.Management.Automation/engine/SessionStateContainer.cs +++ b/src/System.Management.Automation/engine/SessionStateContainer.cs @@ -1324,7 +1324,7 @@ internal void GetChildItems( try { // If we're recursing, do some path fixups to match user - // expectations only if the last part is a file and not a directory: + // expectations, but only if the last part is a file and not a directory: if (recurse && !path.EndsWith(Path.DirectorySeparatorChar) && !path.EndsWith(Path.AltDirectorySeparatorChar)) { string childName = GetChildName(path, context); From 470cdb29bf6a97e5fbd2db0d59eaa525a183c031 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Tue, 19 Jul 2022 22:40:00 -0700 Subject: [PATCH 3/3] address Ilya's feedback removing 2 lines that don't work as expected --- .../namespaces/FileSystemProvider.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 351ea46afde..4e0d72765ce 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1373,10 +1373,7 @@ private FileSystemInfo GetFileSystemItem(string path, ref bool isContainer, bool path = NormalizePath(path); FileInfo result = new FileInfo(path); - // FileInfo.Exists is always false for a directory path, so we check the attribute for existence. var attributes = result.Attributes; - if ((int)attributes == -1) { /* Path doesn't exist. */ return null; } - bool hidden = attributes.HasFlag(FileAttributes.Hidden); isContainer = attributes.HasFlag(FileAttributes.Directory);