Closed
Description
by MartyMacGyver:
As described in bug 3366 (https://golang.org/issue/3366) the os.Rename() function doesn't work in Windows in a way that is consistent with other OSes. In Posix-compliant OSes, the os.Rename() function works properly (and atomically), including when replacing an existing file. In Windows this does not work correctly as designed and fails when replacing an existing file. Since it has been decided that this functionality for Windows will not be implemented in the existing os.Rename(), it is desirable to have an equivalent os.Replace() function that works across platforms. In the case of non-Windows platforms it would be a simple wrapper for os.Rename(), but in the case of Windows it would call the proper OS-level APIs to perform the operation in an equivalent fashion. Other languages have encountered and successfully tackled this same problem in Windows. As one example, the report of the same issue in Python led to a detailed discussion and ultimately had a successful resolution (http://bugs.python.org/issue8828). The workaround used in other Go projects follows the same pattern - effectively creating an os.Replace() function for this circumstance, using the Windows MoveFileEx API for Windows platforms and falling through to os.Rename() for the others. Since os.Rename() cannot be enhanced to handle Windows cases in an equivalent fashion, an os.Replace() function that satisfies the same criteria would prevent developers from re-inventing the same wheel in very similar ways way simply to avoid the disjoint functionality in os.Rename() when it comes to this relatively common use case.
Metadata
Metadata
Assignees
Labels
No labels
Type
Projects
Relationships
Development
No branches or pull requests
Activity
ianlancetaylor commentedon Oct 9, 2014
Comment 1:
Labels changed: added repo-main, release-go1.5.
Status changed to Accepted.
gopherbot commentedon Oct 9, 2014
Comment 2 by MartyMacGyver:
anacrolix commentedon Oct 10, 2014
Comment 3:
gopherbot commentedon Oct 10, 2014
Comment 4 by MartyMacGyver:
Fix file moving to be safe on Windows.
Fix file moving to be safe on Windows.
Working in windows (.cmd instead of .do). Rename fails
7 remaining items
rsc commentedon Nov 5, 2015
This bug was originally created asking for an os.Replace with consistent behavior on Windows, because #3366 (fix os.Rename) was closed as impossible.
I'm changing the topic of this bug to fix os.Rename, effectively replacing #3366. I don't believe package os should be inventing new functionality, but I do think that it should strive for consistent functionality in what's there. (There is for example another open bug for making os.RemoveAll work with read-only files on Windows.)
Based on http://stackoverflow.com/questions/167414/is-an-atomic-file-rename-with-overwrite-possible-on-windows it looks like it may be possible to just fix os.Rename on Windows. In particular, it sounds like ReplaceFile is the right call to make.
If we decide that it is truly impossible to implement os.Rename on Windows then we could fall back to a discussion of a new function, but I'd rather take all the existing code using os.Rename and make it start working than make it obsolete.
[-]os: Create an os.Replace() function that works consistently across all platforms[/-][+]os: make Rename atomic on Windows[/+]kardianos commentedon Nov 6, 2015
This is difficult to determine the correct winapi to call here. There are typically three winapi calls suggested:
MoveFileEx is what go currently has in go1.5 and tip. When this bug was written go used "MoveFile".
MoveFileTransacted has two strikes against it: 1) It is only supported on Vista+ and 2) MS says it may remove all the transacted APIs, including this one.
Is ReplaceFile atomic? ReplaceFile has error code ERROR_UNABLE_TO_MOVE_REPLACEMENT which states:
It also only works on files. However a research paper from MS claims that ReplaceFile is atomic: http://research.microsoft.com/pubs/64525/tr-2006-45.pdf . I suspect that this is a mistake.
MoveFileEx has many flags. Of interest here are two in particular:
MOVEFILE_REPLACE_EXISTING
andMOVEFILE_COPY_ALLOWED
. Currently in go we only useMOVEFILE_REPLACE_EXISTING
and thus Rename will move if attempted between different volumes. However only theMOVEFILE_COPY_ALLOWED
flag suggests multiple operations are used.I think what we currently do is the correct thing to do on Windows.
alexbrainman commentedon Nov 9, 2015
@kardianos didn't you fix issue #3366 with CL 6140? If I am not mistaken, you have even added new TestRenameOverwriteDest that tests the problem raised in issue #3366.
@rsc what exactly do you want us to do?
Alex
kardianos commentedon Nov 9, 2015
@alexbrainman That was my impression as well. I just wanted to be through in my analysis.
rsc commentedon Nov 24, 2015
The original report says:
I interpreted the original report as saying that
fails on Windows because y already exists.
If that is true, then I think we should fix that. If it's not true, then I think there is nothing to do here. I admit that I took the report at face value and did not investigate whether that is true.
I agree @alexbrainman that TestRenameOverwriteDest seems to suggest this example already works. Given that, I think we can close the bug. Sorry for the confusion.
Initial Commit.