diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 8987ab432c..6c1b3bd945 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -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; + /// This member is used for internal test purposes. public static void SetTestHook(string property, bool value) { diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index d5dde6ff71..b8e0418212 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -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 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 8ccf7b31ec..d3964dc8dc 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 @@ -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' {