Fallback to CopyAndDelete() when MoveTo() fails due to an IOException (#15077)

This commit is contained in:
Steve Lee
2021-03-25 15:11:17 -07:00
committed by GitHub
parent 177921ed63
commit 5d0dadf19d
2 changed files with 32 additions and 12 deletions
@@ -51,15 +51,6 @@ namespace Microsoft.PowerShell.Commands
ISecurityDescriptorCmdletProvider,
ICmdletProviderSupportsHelp
{
#if UNIX
// This is the errno returned by the rename() syscall
// when an item is attempted to be renamed across filesystem mount boundaries.
private const int MOVE_FAILED_ERROR = 18;
#else
// 0x80070005 ACCESS_DENIED is returned when trying to move files across volumes like DFS
private const int MOVE_FAILED_ERROR = -2147024891;
#endif
// 4MB gives the best results without spiking the resources on the remote connection for file transfers between pssessions.
// NOTE: The script used to copy file data from session (PSCopyFromSessionHelper) has a
// maximum fragment size value for security. If FILETRANSFERSIZE changes make sure the
@@ -6078,12 +6069,12 @@ namespace Microsoft.PowerShell.Commands
{
if (InternalTestHooks.ThrowExdevErrorOnMoveDirectory)
{
throw new IOException("Invalid cross-device link", hresult: MOVE_FAILED_ERROR);
throw new IOException("Invalid cross-device link");
}
directory.MoveTo(destinationPath);
}
catch (IOException e) when (e.HResult == MOVE_FAILED_ERROR)
catch (IOException)
{
// Rather than try to ascertain whether we can rename a directory ahead of time,
// it's both faster and more correct to try to rename it and fall back to copy/deleting it
@@ -154,7 +154,7 @@ Describe "Basic FileSystem Provider Tests" -Tags "CI" {
}
It "Verify Move-Item will not move to an existing file" {
{ Move-Item -Path $testDir -Destination $testFile -ErrorAction Stop } | Should -Throw -ErrorId "MoveDirectoryItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand"
{ Move-Item -Path $testDir -Destination $testFile -ErrorAction Stop } | Should -Throw -ErrorId 'DirectoryExist,Microsoft.PowerShell.Commands.MoveItemCommand'
$error[0].Exception | Should -BeOfType System.IO.IOException
$testDir | Should -Exist
}
@@ -1500,3 +1500,32 @@ Describe "Verify sub-directory creation under root" -Tag 'CI','RequireSudoOnUnix
$dirPath | Should -Exist
}
}
Describe "Windows admin tests" -Tag 'RequireAdminOnWindows' {
It "Verify Move-Item for directory across drives on Windows" -Skip:(!$IsWindows) {
try {
# find first available drive letter, unfortunately need to use both function: and Win32_LogicalDisk to cover
# both subst drives and bitlocker drives
$drive = Get-ChildItem function:[h-z]: -Name | Where-Object { !(Test-Path -Path $_) -and !(Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='$_'") } | Select-Object -First 1
if ($null -eq $drive) {
throw "Test cannot continue as no drive letter available"
}
$dest = (Resolve-Path -Path $TestDrive).ProviderPath
$null = New-Item -ItemType Directory -Path $dest -Name test
$out = subst $drive $dest 2>&1
if ($LASTEXITCODE -ne 0) {
throw "subst failed with exit code ${LASTEXITCODE} for drive '$drive': $out"
}
$testdir = New-Item -ItemType Directory -Path $drive -Name testmovedir -Force
1 > $testdir\test.txt
Move-Item $drive\testmovedir $dest\test
"$drive\testmovedir" | Should -Not -Exist
"$dest\test\testmovedir\test.txt" | Should -FileContentMatchExactly 1
}
finally {
subst $drive /d
}
}
}