在 Windows 上是否可以重命名(覆盖)原子文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/167414/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 05:28:32  来源:igfitidea点击:

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

windowswinapiposix

提问by teratorn

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

在 POSIX 系统上,rename(2) 提供原子重命名操作,包括覆盖目标文件(如果存在且权限允许)。

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.

有没有办法在 Windows 上获得相同的语义?我知道 Vista 和 Server 2008 上的 MoveFileTransacted(),但我需要它来支持 Win2k 及更高版本。

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?

我看到很多人说这在win32上是不可能的,但我问你,真的吗?

Please provide reliable citations if possible.

如果可能,请提供可靠的引文。

采纳答案by Adam Davis

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.

Win32 不保证原子文件元数据操作。我会提供一个引文,但没有 - 没有书面或书面保证的事实意味着同样多。

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.

您将不得不编写自己的例程来支持这一点。很不幸,但您不能指望 win32 提供这种级别的服务 - 它根本不是为此而设计的。

回答by Adam Davis

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

在 Windows Vista 和 Windows Server 2008 中添加了原子移动功能 - MoveFileTransacted()

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

不幸的是,这对旧版本的 Windows 没有帮助。

Interesting article here on MSDN.

MSDN 上的有趣文章

回答by gbjbaanb

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.

您仍然可以在 Windows 上进行 rename() 调用,但我想在不知道您使用的文件系统的情况下无法做出您想要的保证 - 例如,如果您使用的是 FAT,则无法保证。

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

但是,您可以使用 MoveFileEx 并使用 MOVEFILE_REPLACE_EXISTING 和 MOVEFILE_WRITE_THROUGH 选项。后者在 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.

我知道这不一定与重命名操作相同,但我认为这可能是您获得的最佳保证 - 如果它为文件移动执行此操作,则应该进行更简单的重命名。

回答by Craig Barkhouse

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.

从 Windows 10 1607 开始,NTFS 确实支持原子取代重命名操作。为此,请调用 NtSetInformationFile(..., FileRenameInformationEx, ...) 并指定 FILE_RENAME_POSIX_SEMANTICS 标志。或者等效地在 Win32 中调用 SetFileInformationByHandle(..., FileRenameInfoEx, ...) 并指定 FILE_RENAME_FLAG_POSIX_SEMANTICS 标志。

回答by Violet Giraffe

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

MSDN 文档避免明确说明哪些 API 是原子的,哪些不是,但 Niall Douglas 在他的Cppcon 2015 演讲中指出唯一的原子函数是

SetFileInformationByHandle

SetFileInformationByHandle

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

FILE_RENAME_INFO.ReplaceIfExists设置为true。它从 Windows Vista / 2008 Server 开始可用。

Niall is the author of a highly complicated LLFIO libraryand 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.

Niall 是一个高度复杂的LLFIO 库的作者,并且是文件系统竞争条件方面的专家,所以我相信如果你正在编写一个原子性至关重要的算法,最好是安全而不是抱歉并使用建议的函数,即使ReplaceFile's 中没有描述指出它不是原子的。

回答by sehafoc

A fair number of answers but not the one I was expecting... I had the understanding (perhaps incorrectly) that MoveFile could beatomic 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.

相当多的答案,但不是我所期望的……我理解(可能是错误的)MoveFile可以是原子的,前提是正确的星星对齐,使用了标志,并且源文件系统与目标文件系统相同. 否则,操作将回退到 [Copy->Delete] 文件。

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.

鉴于; 我还了解到 MoveFile —— 当它是原子的 —— 只是设置文件信息,这也可以在这里完成: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)

有人做了一个名为“ Racing the Filesystem”的演讲,其中更深入地讨论了这一点。(大约 2/3rds 他们谈论原子重命名)

回答by Pavel P

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

std::rename并以C++17 std::filesystem::rename开头。如果目的地与std::rename以下内容一起存在,则未指定会发生什么情况:

If new_filename exists, the behavior is implementation-defined.

如果 new_filename 存在,则行为是实现定义的。

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

但是,需要POSIX rename以原子方式替换现有文件

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.

对于常规文件,此 rename() 函数等效于 ISO C 标准定义的函数。它在此处的包含扩展了该定义以包括对目录的操作并指定当新参数命名已存在的文件时的行为。该规范要求函数的动作是原子的。

Thankfully, std::filesystem::renamerequires that it behaves just like POSIX:

值得庆幸的是,std::filesystem::rename要求它的行为就像 POSIX:

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

将 old_p 标识的文件系统对象移动或重命名为 new_p,就像通过 POSIX 重命名一样

However, when I tried to debug, it appears that std::filesystem::renameas 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.

但是,当我尝试调试时,似乎std::filesystem::renameVS2019 实现的(截至 2020 年 3 月)只是调用MoveFileEx,这在某些情况下不是原子的。因此,当其实现中的所有错误都修复后,我们可能会看到可移植的 atomic std::filesystem::rename