Is an atomic file rename (with overwrite) possible on Windows?

WindowsWinapiPosix

Windows Problem Overview


On POSIX systems rename(2) provides for an atomic rename operation, including overwriting of the destination file if it exists and if permissions allow.

Is there any way to get the same semantics on Windows? I know about MoveFileTransacted() on Vista and Server 2008, but I need this to support Win2k and up.

The key word here is atomic... the solution must not be able to fail in any way that leaves the operation in an inconsistent state.

I've seen a lot of people say this is impossible on win32, but I ask you, is it really?

Please provide reliable citations if possible.

Windows Solutions


Solution 1 - Windows

Solution 2 - Windows

Win32 does not guarantee atomic file meta data operations. I'd provide a citation, but there is none - that fact that there's no written or documented guarantee means as much.

You're going to have to write your own routines to support this. It's unfortunate, but you can't expect win32 to provide this level of service - it simply wasn't designed for it.

Solution 3 - Windows

In Windows Vista and Windows Server 2008 an atomic move function has been added - MoveFileTransacted()

Unfortunately this doesn't help with older versions of Windows.

Interesting article here on MSDN.

Solution 4 - Windows

Starting with Windows 10 1607, NTFS does support an atomic superseding rename operation. To do this call NtSetInformationFile(..., FileRenameInformationEx, ...) and specify the FILE_RENAME_POSIX_SEMANTICS flag.

Or equivalently in Win32 call SetFileInformationByHandle(..., FileRenameInfoEx, ...) and specify the FILE_RENAME_FLAG_POSIX_SEMANTICS flag.

Solution 5 - Windows

you still have the rename() call on Windows, though I imagine the guarantees you want cannot be made without knowing the filesystem you're using - no guarantees if you're using FAT for instance.

However, you can use MoveFileEx and use the MOVEFILE_REPLACE_EXISTING and MOVEFILE_WRITE_THROUGH options. The latter has this description in MSDN:

> Setting this value guarantees that a > move performed as a copy and delete > operation is flushed to disk before > the function returns. The flush occurs > at the end of the copy operation.

I know that's not necessarily the same as a rename operation, but I think it might be the best guarantee you'll get - if it does that for a file move, it should for a simpler rename.

Solution 6 - Windows

The MSDN documentation avoids clearly stating which APIs are atomic and which are not, but Niall Douglas states in his Cppcon 2015 talk that the only atomic function is

SetFileInformationByHandle

with FILE_RENAME_INFO.ReplaceIfExists set to true. It's available starting with Windows Vista / 2008 Server.

Niall is the author of a highly complicated LLFIO library and is an expert in file system race conditions so I believe if you're writing an algorithm where atomicity is crucial, better be safe than sorry and use the suggested function even though nothing in ReplaceFile's description states it's not atomic.

Solution 7 - Windows

A fair number of answers but not the one I was expecting... I had the understanding (perhaps incorrectly) that MoveFile could be atomic provided that the proper stars aligned, flags were used, and file system was the same on the source as target. Otherwise, the operation would fall back to a [Copy->Delete]File.

Given that; I was also had the understanding that MoveFile -- when it is atomic -- was just setting the file information which also could be done here: setfileinfobyhandle.

Someone gave a talk called "Racing the Filesystem" which goes into some more depth about this. (about 2/3rds down they talk about atomic rename)

Solution 8 - Windows

There is std::rename and starting with C++17 std::filesystem::rename. It's unspecified what happens if destination exists with std::rename:

> If new_filename exists, the behavior is implementation-defined.

POSIX rename, however, is required to replace existing files atomically:

> This rename() function is equivalent for regular files to that defined > by the ISO C standard. Its inclusion here expands that definition to > include actions on directories and specifies behavior when the new > parameter names a file that already exists. That specification > requires that the action of the function be atomic.

Thankfully, std::filesystem::rename requires that it behaves just like POSIX:

> Moves or renames the filesystem object identified by old_p to new_p as > if by the POSIX rename

However, when I tried to debug, it appears that std::filesystem::rename as implemented by VS2019 (as of March 2020) simply calls MoveFileEx, which isn't atomic in some cases. So, possibly, when all bugs in its implementation are fixed, we'll see portable atomic std::filesystem::rename.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionteratornView Question on Stackoverflow
Solution 1 - WindowsedgView Answer on Stackoverflow
Solution 2 - WindowsAdam DavisView Answer on Stackoverflow
Solution 3 - WindowsAndrewView Answer on Stackoverflow
Solution 4 - WindowsCraig BarkhouseView Answer on Stackoverflow
Solution 5 - WindowsgbjbaanbView Answer on Stackoverflow
Solution 6 - WindowsViolet GiraffeView Answer on Stackoverflow
Solution 7 - WindowssehafocView Answer on Stackoverflow
Solution 8 - WindowsPavel PView Answer on Stackoverflow