在没有 fsync() 的情况下替换现有文件是否“损坏”?

Nat*_*dge 4 io synchronization files failure-resistance

在 Linux 的mount(2)手册页中,我注意到以下摘录:

许多损坏的应用程序在通过模式替换现有文件时不使用 fsync(),例如

fd = open("foo.new")/write(fd,...)/close(fd)/ rename("foo.new", "foo")
Run Code Online (Sandbox Code Playgroud)

或者更糟

fd = open("foo", O_TRUNC)/write(fd,...)/close(fd).
Run Code Online (Sandbox Code Playgroud)

如果启用了 auto_da_alloc,ext4 将检测替换通过重命名和替换通过截断模式并强制分配任何延迟分配块,以便在下一次日志提交时,在默认的 data=ordered 模式下,在提交 rename() 操作之前,新文件被强制写入磁盘。这提供了与 ext3 大致相同级别的保证,并避免了在延迟分配块被强制写入磁盘之前系统崩溃时可能发生的“零长度”问题。

这段代码在什么意义上被“破坏”了?他们是说这段代码是非法的还是不符合标准(POSIX 等)?

显然fsync(),对于担心系统崩溃会发生什么的人来说,这可能是一个好主意。但是假设系统不会崩溃,那么示例代码的两个版本(没有fsync())是否都做正确的事情?

der*_*ert 5

rename预计是原子的:它要么完全完成,要么根本不完成。重命名 A 来代替 B 应该会让你同时保持 A 和 B 完好无损(根本没有发生);或者在名称 B 下只有 A 的内容(它完全完成)。

只要系统不崩溃,无论fsync(等)调用如何,都会发生这种情况。

但是,如果系统确实崩溃了,结果可能是重命名本身命中了磁盘(并因此完成)。请记住,尽管名称 != 文件。文件/inode 可以有多个名称。重命名正在更改名称,而不是基础文件/数据。

所以你可以有你的程序写A,重命名它来替换B,然后断电的状态。结果是文件系统将重命名写入磁盘,而不是 A 中的实际数据。没有fsync. 因此,您最终会得到一个零长度的 B,或者一个零填充的 B。

究其原因的应用程序不会写的临时文件重命名+,而不是只覆盖该文件是因为它想要的碰撞安全性。如果将重要文档的半写临时副本留在未修改好的副本旁边,用户不会太生气。但如果没有好的副本,用户将不高兴。