diff --git a/CHANGELOG.md b/CHANGELOG.md index 850671d..b9ceb6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,5 +57,7 @@ The format is based on 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` +- Fix `Get-FileHash2`, which stopped at a folder in `-Path` and didn't hash + the files that followed it; folders are now skipped [Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD diff --git a/Docs/Cmdlets/Get-FileHash2.md b/Docs/Cmdlets/Get-FileHash2.md index 3eb6d05..3867548 100644 --- a/Docs/Cmdlets/Get-FileHash2.md +++ b/Docs/Cmdlets/Get-FileHash2.md @@ -23,7 +23,7 @@ The `Get-FileHash2` cmdlet calculates the hash value of each file that `-Path` p `-Algorithm` selects the hash algorithm and accepts `SHA1`, `SHA256`, `SHA384`, `SHA512`, `MACTripleDES`, `MD5`, and `RIPEMD160`. The default is `SHA256`. -The cmdlet hashes files only. A path that points to a folder is skipped, and because the cmdlet stops processing the current input when it meets one, a folder in the middle of a `-Path` array suppresses the results of the paths that follow it in the same array. Pass only file paths, or filter folders out before you pipe items into the cmdlet. A path that does not exist produces a non-terminating `ReadFileError`. +The cmdlet hashes files only and skips paths that point to folders. A path that does not exist produces a non-terminating `ReadFileError`. `-Path` accepts pipeline input by value and by property name through its `FullName` alias, so you can pipe the output of `Get-ChildItem2`, `Get-Item2`, or `Get-ChildItem` into the cmdlet; folders that arrive through the pipeline are skipped individually. Because the cmdlet reads files through the AlphaFS library, it also hashes files whose path exceeds the 260-character `MAX_PATH` limit. Relative paths are resolved against the current location. @@ -125,6 +125,8 @@ The hash is returned as an uppercase hexadecimal string without separators, whic `MACTripleDES` is a keyed message authentication code that is created with a key that is generated for each call, so its result is not reproducible across invocations and is not suitable for comparing files. +Before 5.0.0, a folder in a `-Path` array stopped the processing of that array, so the files that followed the folder were not hashed. + ## RELATED LINKS [Get-ChildItem2](Get-ChildItem2.md) diff --git a/NTFSSecurity/MiscCmdlets/GetFileHash2.cs b/NTFSSecurity/MiscCmdlets/GetFileHash2.cs index 1555a4c..b717d2b 100644 --- a/NTFSSecurity/MiscCmdlets/GetFileHash2.cs +++ b/NTFSSecurity/MiscCmdlets/GetFileHash2.cs @@ -48,7 +48,11 @@ namespace NTFSSecurity { item = GetFileSystemInfo2(path) as FileInfo; if (item == null) - return; + { + // Like Get-FileHash, skip folders and continue with the next path. + WriteVerbose(string.Format("Skipping '{0}', which is a folder", path)); + continue; + } } catch (Exception ex) { diff --git a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml index bf7b04d..7e44f86 100644 --- a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml +++ b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml @@ -4042,7 +4042,7 @@ PS C:\> Disable-Privileges The `Get-FileHash2` cmdlet calculates the hash value of each file that `-Path` points to and returns the file object with the result attached. The returned object is the file object of the file, extended with a `Hash` property that holds the hash as an uppercase hexadecimal string and an `Algorithm` property that names the algorithm that was used. The module's formatting data displays those objects as a table with the `Algorithm`, `Hash`, and `FullName` columns. `-Algorithm` selects the hash algorithm and accepts `SHA1`, `SHA256`, `SHA384`, `SHA512`, `MACTripleDES`, `MD5`, and `RIPEMD160`. The default is `SHA256`. - The cmdlet hashes files only. A path that points to a folder is skipped, and because the cmdlet stops processing the current input when it meets one, a folder in the middle of a `-Path` array suppresses the results of the paths that follow it in the same array. Pass only file paths, or filter folders out before you pipe items into the cmdlet. A path that does not exist produces a non-terminating `ReadFileError`. + The cmdlet hashes files only and skips paths that point to folders. A path that does not exist produces a non-terminating `ReadFileError`. `-Path` accepts pipeline input by value and by property name through its `FullName` alias, so you can pipe the output of `Get-ChildItem2`, `Get-Item2`, or `Get-ChildItem` into the cmdlet; folders that arrive through the pipeline are skipped individually. Because the cmdlet reads files through the AlphaFS library, it also hashes files whose path exceeds the 260-character `MAX_PATH` limit. Relative paths are resolved against the current location. @@ -4143,6 +4143,7 @@ PS C:\> Disable-Privileges If the file cannot be opened because access is denied, the cmdlet takes ownership of the file with the account that runs it, calculates the hash, and restores the previous owner afterward. That fallback fails with a `GetHashError` when the account is not allowed to change the owner of the file. The hash is returned as an uppercase hexadecimal string without separators, which differs from the lowercase output of some other hashing tools. Compare hash values case-insensitively. `MACTripleDES` is a keyed message authentication code that is created with a key that is generated for each call, so its result is not reproducible across invocations and is not suitable for comparing files. + Before 5.0.0, a folder in a `-Path` array stopped the processing of that array, so the files that followed the folder were not hashed. diff --git a/Tests/FileHash.Tests.ps1 b/Tests/FileHash.Tests.ps1 new file mode 100644 index 0000000..64d9cba --- /dev/null +++ b/Tests/FileHash.Tests.ps1 @@ -0,0 +1,45 @@ +<# + Tests Get-FileHash2 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 'FileHash' + Push-Location -LiteralPath $sandbox + + $folder = Join-Path -Path $sandbox -ChildPath 'Folder' + $first = Join-Path -Path $sandbox -ChildPath 'One.txt' + $second = Join-Path -Path $sandbox -ChildPath 'Two.txt' + Assert-TestSandboxPath -Sandbox $sandbox -Path $folder, $first, $second + New-Item -ItemType Directory -Path $folder | Out-Null + Set-Content -LiteralPath $first -Value 'One' + Set-Content -LiteralPath $second -Value 'Two' +} + +AfterAll { + Pop-Location + Remove-TestSandbox -Sandbox $sandbox + Remove-Module -Name NTFSSecurity -Force -ErrorAction SilentlyContinue +} + +Describe 'Get-FileHash2' { + Context 'When -Path contains a folder' { + It 'Should skip the folder and hash the files that follow it' { + if ($PSVersionTable.PSEdition -eq 'Core') { + Set-ItResult -Skipped -Because 'Get-FileHash2 fails in PowerShell 7 until it no longer references RIPEMD160' + } + + $results = @(Get-FileHash2 -Path $first, $folder, $second -ErrorVariable hashErrors -ErrorAction SilentlyContinue) + + $hashErrors | Should -BeNullOrEmpty + $results.Name | Should -Be @('One.txt', 'Two.txt') + $results[1].Hash | Should -BeExactly (Get-FileHash -LiteralPath $second -Algorithm SHA256).Hash + } + } +}