mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Fallback to CopyAndDelete() when MoveTo() fails due to an IOException (#15077)
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user