假设您有一个名为foo
的文件,其中包含确定的字节序列X
,并且您想将其原子替换为一个名为bar
的文件,其中该文件包含字节序列{{ 1}}。这通常是通过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
的数据在此过程中已经丢失。答案 0 :(得分:2)
根据您的评论:
用于重复数据删除工具。我想用指向另一个文件的链接替换foo,该文件包含与foo相同的数据,而不会在进程中丢失数据
我认为您有XY问题。您不能使rename
操作相对于文件内容是原子的。但是,您的目标只是避免在重复数据删除过程中文件意外更改时避免数据丢失。这适用于其他方法,例如保持与旧文件的硬链接并在执行重命名后 将其还原(还原为原始名称或特殊的恢复区域),然后比较以检测是否已更改
但是,至少有许多基本问题仍然使该问题成为问题:
一个进程可能具有一个打开的句柄,可以在未修改的情况下写入旧文件,并且可以在删除重复数据后对其进行修改和关闭。在这种情况下,关闭操作将使其孤立,并且数据将丢失。
任何打算修改其中一个重复数据删除文件的进程都将在硬链接后同时修改所有重复项,这可能与您的预期相反。
如果您的目标是重复数据删除以节省空间,但保留语义以允许修改,则您确实需要一个文件系统,该文件系统使用写时复制语义(而不是硬链接)对fs块进行重复数据删除。另一方面,如果要硬链接,则应在重复数据删除操作期间和之后将整个重复数据删除树视为本质上是只读的。