Browse Source

fix: write -PassThru of the *-Item2 cmdlets only for a performed operation

Copy-Item2, Move-Item2, and Remove-Item2 wrote the item with -PassThru
also when -WhatIf or a declined confirmation skipped the operation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
pull/103/head
Raimund Andree 7 days ago
parent
commit
78b0e368f4
  1. 3
      CHANGELOG.md
  2. 2
      Docs/Cmdlets/Copy-Item2.md
  3. 2
      Docs/Cmdlets/Move-Item2.md
  4. 2
      Docs/Cmdlets/Remove-Item2.md
  5. 6
      NTFSSecurity/ItemCmdlets/CopyItem2.cs
  6. 6
      NTFSSecurity/ItemCmdlets/MoveItem2.cs
  7. 6
      NTFSSecurity/ItemCmdlets/RemoveItem2.cs
  8. 3
      NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml
  9. 13
      Tests/ItemCmdlets.Tests.ps1

3
CHANGELOG.md

@ -146,5 +146,8 @@ The format is based on
source path as the destination, and of `Disable-Privileges`, which said
that the privileges were enabled, and the spelling of the privilege in
the warning of `Get-NTFSEffectiveAccess`
- Fix `-PassThru` of `Copy-Item2`, `Move-Item2`, and `Remove-Item2`, which
wrote the item also when `-WhatIf` or a declined confirmation skipped the
operation
[Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD

2
Docs/Cmdlets/Copy-Item2.md

@ -188,6 +188,8 @@ With `-PassThru $true` the cmdlet returns a folder object for each folder that i
`Copy-Item2` copies through the AlphaFS library (`Alphaleonis.Win32.Filesystem`), which is why it handles source and destination paths that exceed the 260-character `MAX_PATH` limit of the built-in `Copy-Item` cmdlet.
Before 5.0.0, `-PassThru` also wrote the item when `-WhatIf` or a declined confirmation skipped the operation.
Before 5.0.0, copying a folder that contained files failed with a `CopyError` that reported a `DirectoryNotFoundException` for the first file in the folder.
If a path in `-Path` does not exist or the destination file exists and `-Force` is missing, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call.

2
Docs/Cmdlets/Move-Item2.md

@ -188,6 +188,8 @@ With `-PassThru $true` the cmdlet returns a folder object for each folder that i
`Move-Item2` moves through the AlphaFS library (`Alphaleonis.Win32.Filesystem`), which is why it handles source and destination paths that exceed the 260-character `MAX_PATH` limit of the built-in `Move-Item` cmdlet.
Before 5.0.0, `-PassThru` also wrote the item when `-WhatIf` or a declined confirmation skipped the operation.
The cmdlet chooses between two mutually exclusive move options. Without `-Force` it moves with `CopyAllowed`, which permits a file to cross volume boundaries because Windows then copies and deletes it. With `-Force` it moves with `ReplaceExisting`, which overwrites the destination but does not request `CopyAllowed`, so a move across volumes can fail when `-Force` is specified.
If a path in `-Path` does not exist or the destination file exists and `-Force` is missing, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call.

2
Docs/Cmdlets/Remove-Item2.md

@ -181,6 +181,8 @@ With `-PassThru` the cmdlet returns a folder object for each folder that it dele
`Remove-Item2` deletes through the AlphaFS library (`Alphaleonis.Win32.Filesystem`), which is why it reaches items whose path exceeds the 260-character `MAX_PATH` limit of the built-in `Remove-Item` cmdlet. Deletion is permanent; the cmdlet does not use the Recycle Bin.
Before 5.0.0, `-PassThru` also wrote the item when `-WhatIf` or a declined confirmation skipped the operation.
The module defines the aliases `rm2` and `del2` for this cmdlet.
A path that does not exist causes the error `FileNotFound`, and a deletion that the file system rejects causes a `DeleteError`. In both cases the cmdlet continues with the next path. Before 5.0.0, a path that did not exist made the cmdlet skip the remaining paths that were passed in the same call.

6
NTFSSecurity/ItemCmdlets/CopyItem2.cs

@ -94,12 +94,15 @@ namespace NTFSSecurity
try
{
var processed = false;
if (item is FileInfo)
{
if (ShouldProcess(resolvedPath, "Copy File"))
{
((FileInfo)item).CopyTo(actualDestination, force ? CopyOptions.None : CopyOptions.FailIfExists, PathFormat.RelativePath);
WriteVerbose(string.Format("File '{0}' copied to '{1}'", resolvedPath, actualDestination));
processed = true;
}
}
else
@ -111,10 +114,11 @@ namespace NTFSSecurity
Directory.CreateDirectory(actualDestination);
((DirectoryInfo)item).CopyTo(actualDestination, force ? CopyOptions.None : CopyOptions.FailIfExists, PathFormat.RelativePath);
WriteVerbose(string.Format("Directory '{0}' copied to '{1}'", resolvedPath, actualDestination));
processed = true;
}
}
if (passThru)
if (passThru && processed)
WriteObject(item);
}
catch (System.IO.IOException ex)

6
NTFSSecurity/ItemCmdlets/MoveItem2.cs

@ -94,12 +94,15 @@ namespace NTFSSecurity
try
{
var processed = false;
if (item is FileInfo)
{
if (ShouldProcess(resolvedPath, "Move File"))
{
((FileInfo)item).MoveTo(actualDestination, force ? MoveOptions.ReplaceExisting : MoveOptions.CopyAllowed, PathFormat.RelativePath);
WriteVerbose(string.Format("File '{0}' moved to '{1}'", resolvedPath, actualDestination));
processed = true;
}
}
else
@ -108,10 +111,11 @@ namespace NTFSSecurity
{
((DirectoryInfo)item).MoveTo(actualDestination, force ? MoveOptions.ReplaceExisting : MoveOptions.CopyAllowed, PathFormat.RelativePath);
WriteVerbose(string.Format("Directory '{0}' moved to '{1}'", resolvedPath, actualDestination));
processed = true;
}
}
if (passThru)
if (passThru && processed)
WriteObject(item);
}
catch (System.IO.IOException ex)

6
NTFSSecurity/ItemCmdlets/RemoveItem2.cs

@ -71,12 +71,15 @@ namespace NTFSSecurity
try
{
var processed = false;
if (item is FileInfo)
{
if (ShouldProcess(item.ToString(), "Remove File"))
{
((FileInfo)item).Delete(force);
WriteVerbose(string.Format("File '{0}' was removed", item.ToString()));
processed = true;
}
}
else
@ -85,10 +88,11 @@ namespace NTFSSecurity
{
((DirectoryInfo)item).Delete(recurse, force);
WriteVerbose(string.Format("Directory '{0}' was removed", item.ToString()));
processed = true;
}
}
if (passThru)
if (passThru && processed)
WriteObject(item);
}
catch (System.IO.IOException ex)

3
NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml

@ -2181,6 +2181,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<maml:alertSet>
<maml:alert>
<maml:para>`Copy-Item2` copies through the AlphaFS library (`Alphaleonis.Win32.Filesystem`), which is why it handles source and destination paths that exceed the 260-character `MAX_PATH` limit of the built-in `Copy-Item` cmdlet.</maml:para>
<maml:para>Before 5.0.0, `-PassThru` also wrote the item when `-WhatIf` or a declined confirmation skipped the operation.</maml:para>
<maml:para>Before 5.0.0, copying a folder that contained files failed with a `CopyError` that reported a `DirectoryNotFoundException` for the first file in the folder.</maml:para>
<maml:para>If a path in `-Path` does not exist or the destination file exists and `-Force` is missing, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call.</maml:para>
</maml:alert>
@ -6964,6 +6965,7 @@ PS C:\Data&gt; Get-NTFSSecurityDescriptor</dev:code>
<maml:alertSet>
<maml:alert>
<maml:para>`Move-Item2` moves through the AlphaFS library (`Alphaleonis.Win32.Filesystem`), which is why it handles source and destination paths that exceed the 260-character `MAX_PATH` limit of the built-in `Move-Item` cmdlet.</maml:para>
<maml:para>Before 5.0.0, `-PassThru` also wrote the item when `-WhatIf` or a declined confirmation skipped the operation.</maml:para>
<maml:para>The cmdlet chooses between two mutually exclusive move options. Without `-Force` it moves with `CopyAllowed`, which permits a file to cross volume boundaries because Windows then copies and deletes it. With `-Force` it moves with `ReplaceExisting`, which overwrites the destination but does not request `CopyAllowed`, so a move across volumes can fail when `-Force` is specified.</maml:para>
<maml:para>If a path in `-Path` does not exist or the destination file exists and `-Force` is missing, the cmdlet writes a non-terminating error and continues with the next path. Before 5.0.0, it skipped the remaining paths that were passed in the same call.</maml:para>
</maml:alert>
@ -7579,6 +7581,7 @@ PS C:\Data&gt; Get-NTFSSecurityDescriptor</dev:code>
<maml:alertSet>
<maml:alert>
<maml:para>`Remove-Item2` deletes through the AlphaFS library (`Alphaleonis.Win32.Filesystem`), which is why it reaches items whose path exceeds the 260-character `MAX_PATH` limit of the built-in `Remove-Item` cmdlet. Deletion is permanent; the cmdlet does not use the Recycle Bin.</maml:para>
<maml:para>Before 5.0.0, `-PassThru` also wrote the item when `-WhatIf` or a declined confirmation skipped the operation.</maml:para>
<maml:para>The module defines the aliases `rm2` and `del2` for this cmdlet.</maml:para>
<maml:para>A path that does not exist causes the error `FileNotFound`, and a deletion that the file system rejects causes a `DeleteError`. In both cases the cmdlet continues with the next path. Before 5.0.0, a path that did not exist made the cmdlet skip the remaining paths that were passed in the same call.</maml:para>
</maml:alert>

13
Tests/ItemCmdlets.Tests.ps1

@ -128,6 +128,19 @@ Describe 'Copy-Item2, Move-Item2, and Remove-Item2 with several paths' {
$messages.Message | Should -Contain ("File '{0}' {1} to '{2}'" -f $first, $Verb, $target)
}
# Before 5.0.0, -PassThru wrote the item also when -WhatIf skipped the operation.
It '<_> should write nothing with -PassThru and -WhatIf' -ForEach @('Copy-Item2', 'Move-Item2', 'Remove-Item2') {
$parameters = @{ Path = $first; PassThru = $true; WhatIf = $true }
if ($_ -ne 'Remove-Item2') {
$parameters.Destination = $destination
}
$result = @(& $_ @parameters)
$result | Should -BeNullOrEmpty
$first | Should -Exist
}
}
Describe 'Copy-Item2' {

Loading…
Cancel
Save