假设您有一个名为 foo
的文件包含一些确定的字节序列 X
,并且您想自动将其替换为名为 bar
的文件包含一个字节序列 Y
.这通常使用 rename()
完成系统调用---在这种情况下,通过调用rename("bar", "foo")
.但是,您希望遵守以下两个约束条件:
- 仅当文件名为
bar
时才应执行替换确实包含数据Y
,否则它应该失败。 - 仅当文件名为
foo
时才应执行替换确实包含数据X
否则它应该失败。
如何正确地做到这一点?
防止foo
和 bar
在我们打电话之前被编辑 rename()
,我们可以用 fnctl
锁定它们或等效的。但是锁只能帮助防止修改文件数据,它们对目录条目没有影响,所以到rename()
做它的魔法,数据foo
或 bar
引用可能不一样。
针对上述两个约束的数据丢失场景的两个示例:
- 我们已锁定名为
bar
的文件并确保它包含数据Y
- 在我们有时间替换之前
foo
与bar
, 一些程序取代了bar
使用以前名为qux
的文件保存数据Z
. - 我们替换
foo
与bar
. - 现在文件
foo
,我们希望它包含bar
的数据, 而是包含qux
的数据.foo
的数据和bar
迷路了。
- 我们已锁定名为
- 我们已锁定名为
foo
的文件并确保它包含数据X
. - 在我们有时间替换之前
foo
与bar
, 一些程序取代了foo
使用以前名为qux
的文件保存数据Z
. - 我们替换
foo
与bar
. - 现在文件
foo
确实包含bar
的数据, 但文件的数据qux
在这个过程中丢失了。
- 我们已锁定名为
最佳答案
根据您的评论:
It's for a deduplication tool. I want to replace foo with a link to another file that holds the same data as foo, without losing data in the process
我认为您遇到了 XY 问题。您不能使 rename
操作相对于文件的内容是原子的。但是您的目标只是避免在重复数据删除过程中文件意外更改时造成数据丢失。这适用于其他方法,例如保留到旧文件的硬链接(hard link)并将其恢复(恢复到原始名称或特殊恢复区) 执行重命名然后比较以检测它是否已更改.
然而,有许多基本问题仍然使这个问题成为问题,至少从以下方面开始:
一个进程可能有一个打开的句柄用于写入旧文件,但尚未对其进行修改,并且可能会在您对它进行重复数据删除后修改并关闭它。在这种情况下,关闭操作会将其孤立并且数据将会丢失。
任何打算修改正在删除重复的文件之一的进程都会在硬链接(hard link)后同时修改所有副本,这可能与您的预期相反。
如果您的目标是通过重复数据删除来节省空间,但保留语义以允许修改,那么您确实需要一个文件系统来通过写时复制语义而不是硬链接(hard link)来对 fs block 进行重复数据删除。另一方面,如果您想要硬链接(hard link),则在重复数据删除操作期间和之后,您应该将被重复数据删除的整个树视为本质上只读的。
关于调用 rename() 而不覆盖意外数据且不覆盖意外数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57081914/