POSIX/Linux 取消链接文件条目可以完全免费吗?
Can POSIX/Linux unlink file entries completely race free?
POSIX 以让进程重命名和取消 link 文件条目而闻名,而不考虑对使用它们的其他人的影响,而 Windows 默认情况下即使您尝试也会引发错误触摸一个目录的时间戳,该目录的文件句柄在内部深处某处打开。
不过Windows没必要这么保守。如果您使用 FILE_FLAG_BACKUP_SEMANTICS 和 FILE_SHARE_DELETE 打开所有文件句柄,并注意在标记删除之前将文件重命名为随机名称,您将获得 POSIX 语义,包括对包含的文件路径的操作没有限制打开文件句柄。
Windows 可以做的一件非常漂亮的事情是仅使用打开的文件描述符执行重命名和删除以及硬 links,因此您可以删除文件而不必担心是否另一个进程已重命名它或文件位置之前路径中的任何目录。此工具可让您执行完全无竞争的文件删除 - 一旦您拥有正确文件的打开文件句柄,您就可以不再关心其他进程对文件系统所做的操作,至少对于删除(这是最重要的,因为它隐式涉及销毁数据)。
这引发了 POSIX 的问题?在 POSIX 上,unlink() 采用路径,并且在使用 /proc/self/fd/x 或 F_GETPATH 检索文件描述符的当前路径和调用 unlink() 之间有人可能更改了该路径,因此可能导致错误的文件被取消linked 和数据丢失。
一个相当安全的解决方案是:
- 使用/proc/self/fd/x或F_GETPATH等获取打开文件描述符的当前路径之一
- 打开它的包含目录。
- 在包含打开文件描述符的叶名的目录上执行 statat(),检查设备 ID 和 inode 是否匹配。
- 如果它们匹配,执行 unlinkat() 删除叶名。
这从父目录向上是安全的,尽管您删除的硬 link 可能不是预期的。但是,如果在包含目录中第三方进程将您的文件重命名为其他名称并将另一个文件重命名为您的叶名,那么在检查 inode 等效性和调用 unlinkat() 之间,这不是竞争安全的。这里可能会删错文件,导致数据丢失。
因此我问这个问题:POSIX 或任何特定的 POSIX 实现(例如 Linux)是否允许程序完全免费取消 link 文件条目?一种解决方案可能是通过打开的文件描述符取消link 一个文件条目,另一种可能是通过 inode 取消link 一个文件条目,但是 google 还没有找到任何一个的解决方案.有趣的是,除了通过打开的文件句柄删除之外,NTFS 确实 允许您通过选择 inode 或 GUID 进行删除(是的,NTFS 确实提供了 inode,您可以从 NT 内核中获取它们),但是这在这里没有多大帮助。
如果这个问题看起来太深奥,这个问题会影响 proposed Boost.AFIO 我需要确定哪些文件系统竞争我可以缓解,哪些我不能缓解,作为其记录的硬行为保证的一部分。
编辑: 阐明打开文件描述符没有规范的当前路径,并且在这个用例中我们不关心 - 我们只想取消link 文件的 link 之一。
没有人回答这个问题,我花了好几天时间浏览 Linux 源代码。我相信答案是 "currently you can't unlink a file race free",所以我在 https://bugzilla.kernel.org/show_bug.cgi?id=93441 打开了一个功能请求,让 Linux 使用 AT_EMPTY_PATH Linux 扩展标志扩展 unlinkat() 。如果他们接受这个想法,我会将此答案标记为正确答案。
POSIX 以让进程重命名和取消 link 文件条目而闻名,而不考虑对使用它们的其他人的影响,而 Windows 默认情况下即使您尝试也会引发错误触摸一个目录的时间戳,该目录的文件句柄在内部深处某处打开。
不过Windows没必要这么保守。如果您使用 FILE_FLAG_BACKUP_SEMANTICS 和 FILE_SHARE_DELETE 打开所有文件句柄,并注意在标记删除之前将文件重命名为随机名称,您将获得 POSIX 语义,包括对包含的文件路径的操作没有限制打开文件句柄。
Windows 可以做的一件非常漂亮的事情是仅使用打开的文件描述符执行重命名和删除以及硬 links,因此您可以删除文件而不必担心是否另一个进程已重命名它或文件位置之前路径中的任何目录。此工具可让您执行完全无竞争的文件删除 - 一旦您拥有正确文件的打开文件句柄,您就可以不再关心其他进程对文件系统所做的操作,至少对于删除(这是最重要的,因为它隐式涉及销毁数据)。
这引发了 POSIX 的问题?在 POSIX 上,unlink() 采用路径,并且在使用 /proc/self/fd/x 或 F_GETPATH 检索文件描述符的当前路径和调用 unlink() 之间有人可能更改了该路径,因此可能导致错误的文件被取消linked 和数据丢失。
一个相当安全的解决方案是:
- 使用/proc/self/fd/x或F_GETPATH等获取打开文件描述符的当前路径之一
- 打开它的包含目录。
- 在包含打开文件描述符的叶名的目录上执行 statat(),检查设备 ID 和 inode 是否匹配。
- 如果它们匹配,执行 unlinkat() 删除叶名。
这从父目录向上是安全的,尽管您删除的硬 link 可能不是预期的。但是,如果在包含目录中第三方进程将您的文件重命名为其他名称并将另一个文件重命名为您的叶名,那么在检查 inode 等效性和调用 unlinkat() 之间,这不是竞争安全的。这里可能会删错文件,导致数据丢失。
因此我问这个问题:POSIX 或任何特定的 POSIX 实现(例如 Linux)是否允许程序完全免费取消 link 文件条目?一种解决方案可能是通过打开的文件描述符取消link 一个文件条目,另一种可能是通过 inode 取消link 一个文件条目,但是 google 还没有找到任何一个的解决方案.有趣的是,除了通过打开的文件句柄删除之外,NTFS 确实 允许您通过选择 inode 或 GUID 进行删除(是的,NTFS 确实提供了 inode,您可以从 NT 内核中获取它们),但是这在这里没有多大帮助。
如果这个问题看起来太深奥,这个问题会影响 proposed Boost.AFIO 我需要确定哪些文件系统竞争我可以缓解,哪些我不能缓解,作为其记录的硬行为保证的一部分。
编辑: 阐明打开文件描述符没有规范的当前路径,并且在这个用例中我们不关心 - 我们只想取消link 文件的 link 之一。
没有人回答这个问题,我花了好几天时间浏览 Linux 源代码。我相信答案是 "currently you can't unlink a file race free",所以我在 https://bugzilla.kernel.org/show_bug.cgi?id=93441 打开了一个功能请求,让 Linux 使用 AT_EMPTY_PATH Linux 扩展标志扩展 unlinkat() 。如果他们接受这个想法,我会将此答案标记为正确答案。