-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Fix FS moves across Unix mounts #13044
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
Conversation
|
Ugh Visual Studio applied some autoformatting... |
9fec65a to
b612197
Compare
b612197 to
771c0dd
Compare
|
Would it be reasonable to try the |
We don’t call that directly, it’s a couple of layers down within .NET. It turns out there’s a similar check done for Windows already with the PowerShell already has its own Implementing that conforms with the existing logic and minimises the code changes required to fix this bug (which we may not be able to test). |
|
A comment in GNU coreutils notes that comparing |
Hmmm, yeah that's good information. I'm also interested by the fact that .NET makes file copying work across drives but not directory copying, making me feel that this might be unintended behaviour. Since their layer is better placed to deal with this, I'll follow up with them before we proceed. |
|
Given the discussion in dotnet/runtime#31149, I don't think .NET is about to fix this, so continuing with a workaround. |
| #else | ||
| // On Windows, being able to rename vs copy/delete a file | ||
| // is just a question of the drive | ||
| if (IsSameWindowsVolume(directory.FullName, destinationPath)) |
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.
.Net uses MOVEFILE_COPY_ALLOWED flag for MoveFileEx() on Windows.
So I think we can remove the check at all on all platforms but only check catch (IOException e) when (e.HResult == UNIX_ERRNO_EXDEV) on Unix-s.
Another thought is that we can a concern about performance (rename on the same volume is very fast but cross-volume one is a coping that can take many time.) and then we need to add a new parameter Move-Item -AllowCrossVolumeCoping to explicitly allow the behavior for all platforms. It is my preference. But how we will detect cross-volume on Windows if root path can be the same?
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.
.Net uses MOVEFILE_COPY_ALLOWED flag for MoveFileEx() on Windows.
That's very interesting. I did a small experiment with PS 7.1 and got this result though:
C:\Users\Robert Holt\Documents\Dev\Microsoft\PSScriptAnalyzer [implement-script-rules +4 ~9 -0 !]
> gi .\tools_copy\ | Sv t
C:\Users\Robert Holt\Documents\Dev\Microsoft\PSScriptAnalyzer [implement-script-rules +4 ~9 -0 !]
> $t.MoveTo('D:\tools')
MethodInvocationException: Exception calling "MoveTo" with "1" argument(s): "Source and destination path must have identical roots. Move will not work across volumes."
C:\Users\Robert Holt\Documents\Dev\Microsoft\PSScriptAnalyzer [implement-script-rules +4 ~9 -0 !]
> get-error
Exception :
Type : System.Management.Automation.MethodInvocationException
ErrorRecord :
Exception :
Type : System.Management.Automation.ParentContainsErrorRecordException
Message : Exception calling "MoveTo" with "1" argument(s): "Source and destination path
must have identical roots. Move will not work across volumes."
HResult : -2146233087
CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
FullyQualifiedErrorId : IOException
InvocationInfo :
ScriptLineNumber : 1
OffsetInLine : 1
HistoryId : -1
Line : $t.MoveTo('D:\tools')
PositionMessage : At line:1 char:1
+ $t.MoveTo('D:\tools')
+ ~~~~~~~~~~~~~~~~~~~~~
CommandOrigin : Internal
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
TargetSite :
Name : ConvertToMethodInvocationException
DeclaringType : System.Management.Automation.ExceptionHandlingOps,
System.Management.Automation, Version=7.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
MemberType : Method
Module : System.Management.Automation.dll
StackTrace :
at System.Management.Automation.ExceptionHandlingOps.ConvertToMethodInvocationException(Exception
exception, Type typeToThrow, String methodName, Int32 numArgs, MemberInfo memberInfo)
at CallSite.Target(Closure , CallSite , Object , String )
at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame
frame)
Message : Exception calling "MoveTo" with "1" argument(s): "Source and destination path
must have identical roots. Move will not work across volumes."
Data : System.Collections.ListDictionaryInternal
InnerException :
Type : System.IO.IOException
TargetSite :
Name : MoveTo
DeclaringType : System.IO.DirectoryInfo
MemberType : Method
Module : System.IO.FileSystem.dll
StackTrace :
at System.IO.DirectoryInfo.MoveTo(String destDirName)
at CallSite.Target(Closure , CallSite , Object , String )
Message : Source and destination path must have identical roots. Move will not work
across volumes.
Source : System.IO.FileSystem
HResult : -2146232800
Source : System.Management.Automation
HResult : -2146233087
CategoryInfo : NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : IOException
InvocationInfo :
ScriptLineNumber : 1
OffsetInLine : 1
HistoryId : -1
Line : $t.MoveTo('D:\tools')
PositionMessage : At line:1 char:1
+ $t.MoveTo('D:\tools')
+ ~~~~~~~~~~~~~~~~~~~~~
CommandOrigin : Internal
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
Another thought is that we can a concern about performance
Personally I think user expectation will be for Move-Item to transparently deal with cross-volume moves without needing to specify a flag. mv doesn't require one and nor does .NET on Windows. Throwing an error is unhelpful and technically not performant -- it performs no useful work. I think the best way to get performance here is probably to optimise your mount layout.
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.
If I remember correctly Windows Explorer on XP warned about cross-volume coping but later (Windows 7?) the behavior was changed. Perhaps we could follow this.
Although we should take into account:
- Performance - coping will be slow which may be unexpected for the user in an interactive session or it is amazing to see a script slowdown.
- Lost security descriptor - after coping permissions are inherited from new root.
- Formally it is a breaking change.
- Reliability - it is not an atom operation.
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.
"user expectation will be for Move-Item to transparently deal with cross-volume moves without needing to specify a flag"
I can't speak for all users but I can confirm that this is what I would expect as a user. :-)
Regarding *nix specific flags, a bit more context of my scenario....The real value of running PowerShell (my opinion of course) on Linux is no longer having to maintain two separate shell script versions (bash and command prompt).
If we start setting a precedence of having to use special flags for Linux or Microsoft or even differently named cmdlets, (Move-LinuxFile Move-MSFile) then as consumers, we likely have to add in if ($Linux) {#Linux ways} else {#Microsoft ways} at which point we might as maintain separate shell scripts.
For the record, throwing errors should always be avoided from a user frustration standpoint but I would be ok if an error was displayed by default and the CopyAndDelete would happen when -Force is provided.
With that said I like @rjmholt's solution and from one dev to another, I appreciate everyone's effort on this one!
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.
@rjmholt Can you remove the check on Windows and check cross-volume move? I expect it is work.
(Although the question is how this works for reparse points like OneDrive but it seems we don't detect this whatever).
Or you already did? If so it looks like a bug in .Net :-(
|
Are there already tests for these scenarios:
|
I'm not familiar with the I'd prefer to keep discussion in this PR scoped, but your contribution of either an issue enumerating those tests or test code itself would be very welcome. |
I'm not currently sure how we would reliably create mount points in our testing infrastructure, especially on the various platforms. I may need help from @adityapatwardhan and @JamesWTruher for that. |
| #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 UNIX_ERRNO_EXDEV = 18; |
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.
18 appears to be correct for Linux, macOS, and FreeBSD.
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.
We should check the error code is 18 for all the variants we support especially Raspbian and Alpine, which could have it a bit different.
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.
Confirmed on RPi:
pi@daedalus:~ $ errno -l | grep EXDEV
EXDEV 18 Invalid cross-device link
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.
Confirmed on Alpine:
/ # errno -l | grep EXDEV
EXDEV 18 Cross-device link
|
🎉 Handy links: |
PR Summary
Fixes #13039.
Currently
Move-Itemdoes not support cross-mount moves on Unix, because it doesn't check properly whether given paths lie on the same filesystem.This PR fixes the check to identify when paths lie on different devices on Unix, so that the move logic can properly copy and delete rather than trying to call the
renameAPI.NOTE I'm not sure how we can test this, but I would love to if possible.
PR Context
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:or[ WIP ]to the beginning of the title (theWIPbot will keep its status check atPendingwhile the prefix is present) and remove the prefix when the PR is ready.