Make Get-ChildItem continue enumeration when encountering error on contained item (#3806)
Added try/catch within the enumeration loop to allow the enumeration to continue after encountering an error such as an item within the directory being deleted or renamed. To assist in testing, two new internal test hooks have been added which cause Get-ChildItem to either delete or rename a specific file (file name hard-coded) when encountered during enumeration.
This commit is contained in:
parent
7aa7f3858c
commit
c29bd7d684
@ -1580,6 +1580,11 @@ namespace System.Management.Automation.Internal
|
||||
// Simulate 'System.Diagnostics.Stopwatch.IsHighResolution is false' to test Get-Uptime throw
|
||||
internal static bool StopwatchIsNotHighResolution;
|
||||
|
||||
// Used in the FileSystemProvider to simulate deleting a file during enumeration in Get-ChildItem
|
||||
internal static bool GciEnumerationActionDelete = false;
|
||||
// Used in the FileSystemProvider to simulate renaming a file during enumeration in Get-ChildItem
|
||||
internal static bool GciEnumerationActionRename = false;
|
||||
|
||||
/// <summary>This member is used for internal test purposes.</summary>
|
||||
public static void SetTestHook(string property, bool value)
|
||||
{
|
||||
|
@ -1677,51 +1677,85 @@ namespace Microsoft.PowerShell.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
bool attributeFilter = true;
|
||||
bool switchAttributeFilter = true;
|
||||
bool filterHidden = false; // "Hidden" is specified somewhere in the expression
|
||||
bool switchFilterHidden = false; // "Hidden" is specified somewhere in the parameters
|
||||
|
||||
if (null != evaluator)
|
||||
// Internal test code, run only if one of the
|
||||
// 'GciEnumerationAction' test hooks are set.
|
||||
if (InternalTestHooks.GciEnumerationActionDelete)
|
||||
{
|
||||
attributeFilter = evaluator.Evaluate(filesystemInfo.Attributes); // expressions
|
||||
filterHidden = evaluator.ExistsInExpression(FileAttributes.Hidden);
|
||||
}
|
||||
if (null != switchEvaluator)
|
||||
{
|
||||
switchAttributeFilter = switchEvaluator.Evaluate(filesystemInfo.Attributes); // switch parameters
|
||||
switchFilterHidden = switchEvaluator.ExistsInExpression(FileAttributes.Hidden);
|
||||
}
|
||||
|
||||
bool hidden = false;
|
||||
if (!Force) hidden = (filesystemInfo.Attributes & FileAttributes.Hidden) != 0;
|
||||
|
||||
// if "Hidden" is explicitly specified anywhere in the attribute filter, then override
|
||||
// default hidden attribute filter.
|
||||
// if specification is to return all containers, then do not do attribute filter on
|
||||
// the containers.
|
||||
bool attributeSatisfy =
|
||||
((attributeFilter && switchAttributeFilter) ||
|
||||
((returnContainers == ReturnContainers.ReturnAllContainers) &&
|
||||
((filesystemInfo.Attributes & FileAttributes.Directory) != 0)));
|
||||
|
||||
if (attributeSatisfy && (filterHidden || switchFilterHidden || Force || !hidden))
|
||||
{
|
||||
if (nameOnly)
|
||||
if (string.Equals(filesystemInfo.Name, "c283d143-2116-4809-bf11-4f7d61613f92", StringComparison.InvariantCulture))
|
||||
{
|
||||
WriteItemObject(
|
||||
filesystemInfo.Name,
|
||||
filesystemInfo.FullName,
|
||||
false);
|
||||
File.Delete(filesystemInfo.FullName);
|
||||
}
|
||||
else
|
||||
}
|
||||
else if (InternalTestHooks.GciEnumerationActionRename)
|
||||
{
|
||||
if (string.Equals(filesystemInfo.Name, "B1B691A9-B7B1-4584-AED7-5259511BEEC4", StringComparison.InvariantCulture))
|
||||
{
|
||||
if (filesystemInfo is FileInfo)
|
||||
WriteItemObject(filesystemInfo, filesystemInfo.FullName, false);
|
||||
var newFullName = Path.Combine(directory.FullName, "77efd2bb-92aa-4ad3-979a-18936a4bd565");
|
||||
File.Move(filesystemInfo.FullName, newFullName);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bool attributeFilter = true;
|
||||
bool switchAttributeFilter = true;
|
||||
// 'Hidden' is specified somewhere in the expression
|
||||
bool filterHidden = false;
|
||||
// 'Hidden' is specified somewhere in the parameters
|
||||
bool switchFilterHidden = false;
|
||||
|
||||
if (null != evaluator)
|
||||
{
|
||||
attributeFilter = evaluator.Evaluate(filesystemInfo.Attributes);
|
||||
filterHidden = evaluator.ExistsInExpression(FileAttributes.Hidden);
|
||||
}
|
||||
if (null != switchEvaluator)
|
||||
{
|
||||
switchAttributeFilter = switchEvaluator.Evaluate(filesystemInfo.Attributes);
|
||||
switchFilterHidden = switchEvaluator.ExistsInExpression(FileAttributes.Hidden);
|
||||
}
|
||||
|
||||
bool hidden = false;
|
||||
if (!Force)
|
||||
{
|
||||
hidden = (filesystemInfo.Attributes & FileAttributes.Hidden) != 0;
|
||||
}
|
||||
|
||||
// If 'Hidden' is explicitly specified anywhere in the attribute filter, then override
|
||||
// default hidden attribute filter.
|
||||
// If specification is to return all containers, then do not do attribute filter on
|
||||
// the containers.
|
||||
bool attributeSatisfy =
|
||||
((attributeFilter && switchAttributeFilter) ||
|
||||
((returnContainers == ReturnContainers.ReturnAllContainers) &&
|
||||
((filesystemInfo.Attributes & FileAttributes.Directory) != 0)));
|
||||
|
||||
if (attributeSatisfy && (filterHidden || switchFilterHidden || Force || !hidden))
|
||||
{
|
||||
if (nameOnly)
|
||||
{
|
||||
WriteItemObject(
|
||||
filesystemInfo.Name,
|
||||
filesystemInfo.FullName,
|
||||
false);
|
||||
}
|
||||
else
|
||||
WriteItemObject(filesystemInfo, filesystemInfo.FullName, true);
|
||||
{
|
||||
if (filesystemInfo is FileInfo)
|
||||
WriteItemObject(filesystemInfo, filesystemInfo.FullName, false);
|
||||
else
|
||||
WriteItemObject(filesystemInfo, filesystemInfo.FullName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.IO.FileNotFoundException ex)
|
||||
{
|
||||
WriteError(new ErrorRecord(ex, "DirIOError", ErrorCategory.ReadError, directory.FullName));
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
WriteError(new ErrorRecord(ex, "DirUnauthorizedAccessError", ErrorCategory.PermissionDenied, directory.FullName));
|
||||
}
|
||||
}// foreach
|
||||
}// foreach
|
||||
|
||||
|
@ -4,12 +4,18 @@ Describe "Get-ChildItem" -Tags "CI" {
|
||||
|
||||
BeforeAll {
|
||||
# Create Test data
|
||||
$null = New-Item -Path $TestDrive -Name "a" -ItemType "File" -Force
|
||||
$null = New-Item -Path $TestDrive -Name "B" -ItemType "File" -Force
|
||||
$null = New-Item -Path $TestDrive -Name "c" -ItemType "File" -Force
|
||||
$null = New-Item -Path $TestDrive -Name "D" -ItemType "File" -Force
|
||||
$null = New-Item -Path $TestDrive -Name "E" -ItemType "Directory" -Force
|
||||
$null = New-Item -Path $TestDrive -Name ".F" -ItemType "File" -Force | %{$_.Attributes = "hidden"}
|
||||
$item_a = "a3fe710a-31af-4834-bc29-d0b584589838"
|
||||
$item_B = "B1B691A9-B7B1-4584-AED7-5259511BEEC4"
|
||||
$item_c = "c283d143-2116-4809-bf11-4f7d61613f92"
|
||||
$item_D = "D39B4FD9-3E1D-4DD5-8718-22FE2C934CE3"
|
||||
$item_E = "EE150FEB-0F21-4AFF-8066-AF59E925810C"
|
||||
$item_F = ".F81D8514-8862-4227-B041-0529B1656A43"
|
||||
$null = New-Item -Path $TestDrive -Name $item_a -ItemType "File" -Force
|
||||
$null = New-Item -Path $TestDrive -Name $item_B -ItemType "File" -Force
|
||||
$null = New-Item -Path $TestDrive -Name $item_c -ItemType "File" -Force
|
||||
$null = New-Item -Path $TestDrive -Name $item_D -ItemType "File" -Force
|
||||
$null = New-Item -Path $TestDrive -Name $item_E -ItemType "Directory" -Force
|
||||
$null = New-Item -Path $TestDrive -Name $item_F -ItemType "File" -Force | %{$_.Attributes = "hidden"}
|
||||
}
|
||||
|
||||
It "Should list the contents of the current folder" {
|
||||
@ -34,25 +40,25 @@ Describe "Get-ChildItem" -Tags "CI" {
|
||||
|
||||
It "Should list files in sorted order" {
|
||||
$files = Get-ChildItem -Path $TestDrive
|
||||
$files[0].Name | Should Be "E"
|
||||
$files[1].Name | Should Be "a"
|
||||
$files[2].Name | Should Be "B"
|
||||
$files[3].Name | Should Be "c"
|
||||
$files[4].Name | Should Be "D"
|
||||
$files[0].Name | Should Be $item_E
|
||||
$files[1].Name | Should Be $item_a
|
||||
$files[2].Name | Should Be $item_B
|
||||
$files[3].Name | Should Be $item_c
|
||||
$files[4].Name | Should Be $item_D
|
||||
}
|
||||
|
||||
It "Should list hidden files as well when 'Force' parameter is used" {
|
||||
$files = Get-ChildItem -path $TestDrive -Force
|
||||
$files | Should not be $null
|
||||
$files.Count | Should be 6
|
||||
$files.Name.Contains(".F")
|
||||
$files.Name.Contains($item_F) | Should Be $true
|
||||
}
|
||||
|
||||
It "Should list only hidden files when 'Hidden' parameter is used" {
|
||||
$files = Get-ChildItem -path $TestDrive -Hidden
|
||||
$files | Should not be $null
|
||||
$files.Count | Should be 1
|
||||
$files[0].Name | Should Be ".F"
|
||||
$files[0].Name | Should Be $item_F
|
||||
}
|
||||
It "Should give .sys file if the fullpath is specified with hidden and force parameter" -Skip:(!$IsWindows){
|
||||
$file = Get-ChildItem -path "$env:SystemDrive\\pagefile.sys" -Hidden
|
||||
@ -60,6 +66,42 @@ Describe "Get-ChildItem" -Tags "CI" {
|
||||
$file.Count | Should be 1
|
||||
$file.Name | Should be "pagefile.sys"
|
||||
}
|
||||
It "Should continue enumerating a directory when a contained item is deleted" {
|
||||
$Error.Clear()
|
||||
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook("GciEnumerationActionDelete", $true)
|
||||
$result = Get-ChildItem -Path $TestDrive -ErrorAction SilentlyContinue
|
||||
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook("GciEnumerationActionDelete", $false)
|
||||
if ($IsWindows)
|
||||
{
|
||||
$Error.Count | Should BeExactly 0
|
||||
$result.Count | Should BeExactly 5
|
||||
}
|
||||
else
|
||||
{
|
||||
$Error.Count | Should BeExactly 1
|
||||
$Error[0].FullyQualifiedErrorId | Should BeExactly "DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand"
|
||||
$Error[0].Exception | Should BeOfType System.Io.FileNotFoundException
|
||||
$result.Count | Should BeExactly 4
|
||||
}
|
||||
}
|
||||
It "Should continue enumerating a directory when a contained item is renamed" {
|
||||
$Error.Clear()
|
||||
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook("GciEnumerationActionRename", $true)
|
||||
$result = Get-ChildItem -Path $TestDrive -ErrorAction SilentlyContinue
|
||||
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook("GciEnumerationActionRename", $false)
|
||||
if ($IsWindows)
|
||||
{
|
||||
$Error.Count | Should BeExactly 0
|
||||
$result.Count | Should BeExactly 4
|
||||
}
|
||||
else
|
||||
{
|
||||
$Error.Count | Should BeExactly 1
|
||||
$Error[0].FullyQualifiedErrorId | Should BeExactly "DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand"
|
||||
$Error[0].Exception | Should BeOfType System.Io.FileNotFoundException
|
||||
$result.Count | Should BeExactly 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Env: Provider' {
|
||||
|
Loading…
Reference in New Issue
Block a user