-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Change behavior of Remove-Item on symbolic links (#621) #3637
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ad1a63e
be795b0
f988991
b5808fb
ed4e77d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2863,15 +2863,6 @@ private void RemoveDirectoryInfoItem(DirectoryInfo directory, bool recurse, bool | |
| continueRemoval = ShouldProcess(directory.FullName, action); | ||
| } | ||
|
|
||
| //if this is a reparse point and force is not specified then warn user but dont remove the directory. | ||
| if (Platform.IsWindows && ((directory.Attributes & FileAttributes.ReparsePoint) != 0) && !Force) | ||
| { | ||
| String error = StringUtil.Format(FileSystemProviderStrings.DirectoryReparsePoint, directory.FullName); | ||
| Exception e = new IOException(error); | ||
| WriteError(new ErrorRecord(e, "DirectoryNotEmpty", ErrorCategory.WriteError, directory)); | ||
| return; | ||
| } | ||
|
|
||
| if ((directory.Attributes & FileAttributes.ReparsePoint) != 0) | ||
| { | ||
| bool success = InternalSymbolicLinkLinkCodeMethods.DeleteJunction(directory.FullName); | ||
|
|
@@ -3327,13 +3318,14 @@ protected override bool HasChildItems(string path) | |
| path = NormalizePath(path); | ||
|
|
||
| // First check to see if it is a directory | ||
|
|
||
| try | ||
| { | ||
| DirectoryInfo directory = new DirectoryInfo(path); | ||
|
|
||
| // If the above didn't throw an exception, check to | ||
| // see if it contains any directories | ||
| // see if we should proceed and if it contains any children | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bit surprising. What case does this cover where an exception is not thrown but Attributes is not Directory? Please add a comment to clarify.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The The myriad other ways the constructor will fail are explicitly called out in the various |
||
| if ((directory.Attributes & FileAttributes.Directory) != FileAttributes.Directory) | ||
| return false; | ||
|
|
||
| result = DirectoryInfoHasChildItems(directory); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -246,6 +246,186 @@ Describe "Basic FileSystem Provider Tests" -Tags "CI" { | |
| } | ||
| } | ||
|
|
||
| Describe "Hard link and symbolic link tests" -Tags "CI", "RequireAdminOnWindows" { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your description mentions that the -Recurse parameter switch was ignored. But I don't see any tests to verify that it is fixed.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a test using the -Recurse parameter when removing a symlink to a directory |
||
| BeforeAll { | ||
| # on macOS, the /tmp directory is a symlink, so we'll resolve it here | ||
| $TestPath = $TestDrive | ||
| if ($IsOSX) | ||
| { | ||
| $item = Get-Item $TestPath | ||
| $dirName = $item.BaseName | ||
| $item = Get-Item $item.PSParentPath | ||
| if ($item.LinkType -eq "SymbolicLink") | ||
| { | ||
| $TestPath = Join-Path $item.Target $dirName | ||
| } | ||
| } | ||
|
|
||
| $realFile = Join-Path $TestPath "file.txt" | ||
| $nonFile = Join-Path $TestPath "not-a-file" | ||
| $fileContent = "some text" | ||
| $realDir = Join-Path $TestPath "subdir" | ||
| $nonDir = Join-Path $TestPath "not-a-dir" | ||
| $hardLinkToFile = Join-Path $TestPath "hard-to-file.txt" | ||
| $symLinkToFile = Join-Path $TestPath "sym-link-to-file.txt" | ||
| $symLinkToDir = Join-Path $TestPath "sym-link-to-dir" | ||
| $symLinkToNothing = Join-Path $TestPath "sym-link-to-nowhere" | ||
| $dirSymLinkToDir = Join-Path $TestPath "symd-link-to-dir" | ||
| $junctionToDir = Join-Path $TestPath "junction-to-dir" | ||
|
|
||
| New-Item -ItemType File -Path $realFile -Value $fileContent >$null | ||
| New-Item -ItemType Directory -Path $realDir >$null | ||
| } | ||
|
|
||
| Context "New-Item and hard/symbolic links" { | ||
| It "New-Item can create a hard link to a file" { | ||
| New-Item -ItemType HardLink -Path $hardLinkToFile -Value $realFile | ||
| Test-Path $hardLinkToFile | Should Be $true | ||
| $link = Get-Item -Path $hardLinkToFile | ||
| $link.LinkType | Should BeExactly "HardLink" | ||
| Get-Content -Path $hardLinkToFile | Should be $fileContent | ||
| } | ||
| It "New-Item can create symbolic link to file" { | ||
| New-Item -ItemType SymbolicLink -Path $symLinkToFile -Value $realFile | ||
| Test-Path $symLinkToFile | Should Be $true | ||
| $real = Get-Item -Path $realFile | ||
| $link = Get-Item -Path $symLinkToFile | ||
| $link.LinkType | Should BeExactly "SymbolicLink" | ||
| $link.Target | Should Be $real.FullName | ||
| Get-Content -Path $symLinkToFile | Should be $fileContent | ||
| } | ||
| It "New-Item can create a symbolic link to nothing" { | ||
| New-Item -ItemType SymbolicLink -Path $symLinkToNothing -Value $nonFile | ||
| Test-Path $symLinkToNothing | Should Be $true | ||
| $link = Get-Item -Path $symLinkToNothing | ||
| $link.LinkType | Should BeExactly "SymbolicLink" | ||
| $link.Target | Should Be $nonFile | ||
| } | ||
| It "New-Item can create a symbolic link to a directory" -Skip:($IsWindows) { | ||
| New-Item -ItemType SymbolicLink -Path $symLinkToDir -Value $realDir | ||
| Test-Path $symLinkToDir | Should Be $true | ||
| $real = Get-Item -Path $realDir | ||
| $link = Get-Item -Path $symLinkToDir | ||
| $link.LinkType | Should BeExactly "SymbolicLink" | ||
| $link.Target | Should Be $real.FullName | ||
| } | ||
| It "New-Item can create a directory symbolic link to a directory" -Skip:(-Not $IsWindows) { | ||
| New-Item -ItemType SymbolicLink -Path $symLinkToDir -Value $realDir | ||
| Test-Path $symLinkToDir | Should Be $true | ||
| $real = Get-Item -Path $realDir | ||
| $link = Get-Item -Path $symLinkToDir | ||
| $link | Should BeOfType System.IO.DirectoryInfo | ||
| $link.LinkType | Should BeExactly "SymbolicLink" | ||
| $link.Target | Should Be $real.FullName | ||
| } | ||
| It "New-Item can create a directory junction to a directory" -Skip:(-Not $IsWindows) { | ||
| New-Item -ItemType Junction -Path $junctionToDir -Value $realDir | ||
| Test-Path $junctionToDir | Should Be $true | ||
| } | ||
| } | ||
|
|
||
| Context "Remove-Item and hard/symbolic links" { | ||
| BeforeAll { | ||
| $testCases = @( | ||
| @{ | ||
| Name = "Remove-Item can remove a hard link to a file" | ||
| Link = $hardLinkToFile | ||
| Target = $realFile | ||
| } | ||
| @{ | ||
| Name = "Remove-Item can remove a symbolic link to a file" | ||
| Link = $symLinkToFile | ||
| Target = $realFile | ||
| } | ||
| ) | ||
|
|
||
| # New-Item on Windows will not create a "plain" symlink to a directory | ||
| $unixTestCases = @( | ||
| @{ | ||
| Name = "Remove-Item can remove a symbolic link to a directory on Unix" | ||
| Link = $symLinkToDir | ||
| Target = $realDir | ||
| } | ||
| ) | ||
|
|
||
| # Junctions and directory symbolic links are Windows and NTFS only | ||
| $windowsTestCases = @( | ||
| @{ | ||
| Name = "Remove-Item can remove a symbolic link to a directory on Windows" | ||
| Link = $symLinkToDir | ||
| Target = $realDir | ||
| } | ||
| @{ | ||
| Name = "Remove-Item can remove a directory symbolic link to a directory on Windows" | ||
| Link = $dirSymLinkToDir | ||
| Target = $realDir | ||
| } | ||
| @{ | ||
| Name = "Remove-Item can remove a junction to a directory" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "on Windows" skipped 😄 |
||
| Link = $junctionToDir | ||
| Target = $realDir | ||
| } | ||
| ) | ||
|
|
||
| function TestRemoveItem | ||
| { | ||
| Param ( | ||
| [string]$Link, | ||
| [string]$Target | ||
| ) | ||
|
|
||
| Remove-Item -Path $Link -ErrorAction SilentlyContinue >$null | ||
| Test-Path -Path $Link | Should Be $false | ||
| Test-Path -Path $Target | Should Be $true | ||
| } | ||
| } | ||
|
|
||
| It "<Name>" -TestCases $testCases { | ||
| Param ( | ||
| [string]$Name, | ||
| [string]$Link, | ||
| [string]$Target | ||
| ) | ||
|
|
||
| TestRemoveItem $Link $Target | ||
| } | ||
|
|
||
| It "<Name>" -TestCases $unixTestCases -Skip:($IsWindows) { | ||
| Param ( | ||
| [string]$Name, | ||
| [string]$Link, | ||
| [string]$Target | ||
| ) | ||
|
|
||
| TestRemoveItem $Link $Target | ||
| } | ||
|
|
||
| It "<Name>" -TestCases $windowsTestCases -Skip:(-not $IsWindows) { | ||
| Param ( | ||
| [string]$Name, | ||
| [string]$Link, | ||
| [string]$Target | ||
| ) | ||
|
|
||
| TestRemoveItem $Link $Target | ||
| } | ||
|
|
||
| It "Remove-Item ignores -Recurse switch when deleting symlink to directory" { | ||
| $folder = Join-Path $TestDrive "folder" | ||
| $file = Join-Path $TestDrive "folder" "file" | ||
| $link = Join-Path $TestDrive "sym-to-folder" | ||
| New-Item -ItemType Directory -Path $folder >$null | ||
| New-Item -ItemType File -Path $file -Value "some content" >$null | ||
| New-Item -ItemType SymbolicLink -Path $link -value $folder >$null | ||
| $childA = Get-Childitem $folder | ||
| Remove-Item -Path $link -Recurse | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not quite clear on this one. This test shows that when we use |
||
| $childB = Get-ChildItem $folder | ||
| $childB.Count | Should Be 1 | ||
| $childB.Count | Should BeExactly $childA.Count | ||
| $childB.Name | Should BeExactly $childA.Name | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Describe "Copy-Item can avoid copying an item onto itself" -Tags "CI", "RequireAdminOnWindows" { | ||
| BeforeAll { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this removed. It seems like the -Force parameter was required before removing and was Windows only. So I assume removing this requirement makes Windows and Linux behavior the same. Please make this clear in the PR description.
Also if we remove this code we should also remove the associated FileSystemProviderStrings.DirectoryReparsePoint message string since it is no longer used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated the PR description and removed the unused DirectoryReparsePoint message string.