From d2c3d04b072cc92047aa7f7c782278c37f863554 Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Mon, 5 Oct 2026 00:06:42 +0200 Subject: [PATCH] fix: return the file when Get-ChildItem2 gets a file path Defect 2. Get-ChildItem2 cast every -Path item to DirectoryInfo, so a file path stopped the cmdlet with an InvalidCastException, a terminating error that also skipped the remaining paths. Like Get-ChildItem, a file path now returns the file itself, filtered like the other items; with -Directory it returns nothing. Tests/ItemCmdlets.Tests.ps1 (new): 3 tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- CHANGELOG.md | 2 + Docs/Cmdlets/Get-ChildItem2.md | 4 +- NTFSSecurity/ItemCmdlets/GetChildItem2.cs | 16 ++++-- NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml | 5 +- Tests/ItemCmdlets.Tests.ps1 | 57 ++++++++++++++++++++ 5 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 Tests/ItemCmdlets.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index e75f892..850671d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,5 +55,7 @@ The format is based on - Fix `Set-NTFSInheritance`, which failed with "Nullable object must have a value" when `-AccessInheritanceEnabled` or `-AuditInheritanceEnabled` was omitted; an omitted parameter now leaves its section unchanged +- Fix `Get-ChildItem2`, which stopped with an `InvalidCastException` when + `-Path` pointed to a file; it now returns the file, like `Get-ChildItem` [Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD diff --git a/Docs/Cmdlets/Get-ChildItem2.md b/Docs/Cmdlets/Get-ChildItem2.md index 477c3ae..5ae3191 100644 --- a/Docs/Cmdlets/Get-ChildItem2.md +++ b/Docs/Cmdlets/Get-ChildItem2.md @@ -182,7 +182,7 @@ Accept wildcard characters: False ### -Path -Specifies the folders whose content you want to list. Relative paths are resolved against the current location, and wildcard characters are not supported. If you omit this parameter, the cmdlet lists the current location. A value that points to a file instead of a folder produces an error. +Specifies the folders whose content you want to list. Relative paths are resolved against the current location, and wildcard characters are not supported. If you omit this parameter, the cmdlet lists the current location. A value that points to a file returns that file, like `Get-ChildItem`, unless you use `-Directory`. ```yaml Type: String[] @@ -305,6 +305,8 @@ The `PrivateData` section of the module manifest `NTFSSecurity.psd1` contains tw A folder that cannot be read produces a non-terminating error with the ID `DirUnauthorizedAccessError` for an access denial or `DirUnspecifiedError` for any other failure, and a path that does not exist produces the error `FileNotFound`. In each case the cmdlet continues with the next path. Failures that occur while `-Recurse` collects the subfolders of a folder are reported as verbose messages only, not as errors. +Before 5.0.0, a `-Path` value that points to a file stopped the cmdlet with an `InvalidCastException`. + ## RELATED LINKS [Get-Item2](Get-Item2.md) diff --git a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs index 788c035..96f08f1 100644 --- a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs +++ b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs @@ -148,12 +148,11 @@ namespace NTFSSecurity { foreach (var path in paths) { - DirectoryInfo di = null; + FileSystemInfo item = null; try { - di = (DirectoryInfo)GetFileSystemInfo2(path); - + item = GetFileSystemInfo2(path); } catch (System.IO.FileNotFoundException ex) { @@ -161,6 +160,17 @@ namespace NTFSSecurity continue; } + var di = item as DirectoryInfo; + if (di == null) + { + // Like Get-ChildItem, a file path returns the file itself. + if (!directory) + { + WriteFileSystemInfoCollection(new FileSystemInfo[] { item }.GetEnumerator()); + } + continue; + } + try { WriteFileSystem(di, 0); diff --git a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml index e59d3a7..bf7b04d 100644 --- a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml +++ b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml @@ -3482,7 +3482,7 @@ PS C:\> Disable-Privileges Path - Specifies the folders whose content you want to list. Relative paths are resolved against the current location, and wildcard characters are not supported. If you omit this parameter, the cmdlet lists the current location. A value that points to a file instead of a folder produces an error. + Specifies the folders whose content you want to list. Relative paths are resolved against the current location, and wildcard characters are not supported. If you omit this parameter, the cmdlet lists the current location. A value that points to a file returns that file, like `Get-ChildItem`, unless you use `-Directory`. String[] @@ -3734,7 +3734,7 @@ PS C:\> Disable-Privileges Path - Specifies the folders whose content you want to list. Relative paths are resolved against the current location, and wildcard characters are not supported. If you omit this parameter, the cmdlet lists the current location. A value that points to a file instead of a folder produces an error. + Specifies the folders whose content you want to list. Relative paths are resolved against the current location, and wildcard characters are not supported. If you omit this parameter, the cmdlet lists the current location. A value that points to a file returns that file, like `Get-ChildItem`, unless you use `-Directory`. String[] @@ -3838,6 +3838,7 @@ PS C:\> Disable-Privileges The module defines the alias `dir2` for this cmdlet. The `PrivateData` section of the module manifest `NTFSSecurity.psd1` contains two settings that this cmdlet reads when it starts. `GetFileSystemModeProperty` adds the calculated `Mode` property to every item. `IdentifyHardLinks` adds the `HardLinkCount` property to every file, which requires an extra call into the file system for each file and therefore slows down large listings noticeably. Set either value to `$false` in the manifest and import the module again if you prefer the faster enumeration over the additional properties. A folder that cannot be read produces a non-terminating error with the ID `DirUnauthorizedAccessError` for an access denial or `DirUnspecifiedError` for any other failure, and a path that does not exist produces the error `FileNotFound`. In each case the cmdlet continues with the next path. Failures that occur while `-Recurse` collects the subfolders of a folder are reported as verbose messages only, not as errors. + Before 5.0.0, a `-Path` value that points to a file stopped the cmdlet with an `InvalidCastException`. diff --git a/Tests/ItemCmdlets.Tests.ps1 b/Tests/ItemCmdlets.Tests.ps1 new file mode 100644 index 0000000..75af9ec --- /dev/null +++ b/Tests/ItemCmdlets.Tests.ps1 @@ -0,0 +1,57 @@ +<# + Tests the long-path item cmdlets of the module built in NTFSSecurity\bin\Release on files in a sandbox folder. +#> +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.' +)] +param () + +BeforeAll { + Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force + $modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\NTFSSecurity\bin\Release\NTFSSecurity.psd1' + Import-Module -Name $modulePath -Force -ErrorAction Stop + $sandbox = New-TestSandbox -Name 'ItemCmdlets' + Push-Location -LiteralPath $sandbox +} + +AfterAll { + Pop-Location + Remove-TestSandbox -Sandbox $sandbox + Remove-Module -Name NTFSSecurity -Force -ErrorAction SilentlyContinue +} + +Describe 'Get-ChildItem2' { + Context 'When -Path is a file' { + BeforeAll { + $folder = Join-Path -Path $sandbox -ChildPath 'ChildItem' + $file = Join-Path -Path $folder -ChildPath 'File.txt' + $other = Join-Path -Path $folder -ChildPath 'Folder\Other.txt' + Assert-TestSandboxPath -Sandbox $sandbox -Path $folder, $file, $other + New-Item -ItemType Directory -Path (Split-Path -Path $other -Parent) -Force | Out-Null + Set-Content -LiteralPath $file -Value 'File' + Set-Content -LiteralPath $other -Value 'Other' + } + + It 'Should return the file itself, like Get-ChildItem' { + $items = @(Get-ChildItem2 -Path $file -ErrorVariable childItemErrors -ErrorAction SilentlyContinue) + + $childItemErrors | Should -BeNullOrEmpty + $items | Should -HaveCount 1 + $items[0] | Should -BeOfType [Alphaleonis.Win32.Filesystem.FileInfo] + $items[0].Name | Should -BeExactly 'File.txt' + } + + It 'Should continue with the next path after a file' { + $items = @(Get-ChildItem2 -Path $file, (Join-Path -Path $folder -ChildPath 'Folder') -ErrorAction SilentlyContinue) + + $items.Name | Should -Be @('File.txt', 'Other.txt') + } + + It 'Should return nothing for a file with -Directory' { + $items = @(Get-ChildItem2 -Path $file -Directory -ErrorVariable childItemErrors -ErrorAction SilentlyContinue) + + $childItemErrors | Should -BeNullOrEmpty + $items | Should -BeNullOrEmpty + } + } +}