移动要附加的文件是否安全?


28

我有一个node.js流程,用于fs.appendFile向添加行file.log。仅附加每行约40个字符的完整行,例如,调用为like fs.appendFile("start-end"),而不是2个调用为fs.appendFile("start-")and fs.appendFile("end")。如果我将此文件移到,file2.log可以确定没有任何行丢失或部分复制吗?

Answers:


36

只要您不跨文件系统边界移动文件,该操作就应该是安全的。这是由于机制“移动”实际上是如何完成的。

如果您mv使用同一文件系统上的文件,则实际上不会触摸该文件,而只会更改文件系统条目。

$ mv foo bar

实际上做

$ ln foo bar
$ rm foo

这将为命名的文件(实际上是文件系统条目指向的inode)创建链接(第二个目录条目),并删除该条目。从现在开始,当删除时,还有第二个文件系统条目指向的inode,删除旧条目实际上并不会删除属于该inode的任何块。foobarfoofoofoofoo

您的程序无论如何都会愉快地追加到文件,因为其打开的文件句柄指向文件的索引节点,而不是文件系统条目。

注意:如果您的程序在两次写入之间关闭并重新打开文件,则最终将使用旧的文件系统条目创建一个文件!

跨文件系统移动:

如果跨文件系统边界移动文件,则情况将变得很糟。在这种情况下,您不能保证文件保持一致,因为mv实际上

  • 在目标文件系统上创建一个新文件
  • 将旧文件的内容复制到新文件
  • 删除旧文件

要么

$ cp /path/to/foo /path/to/bar
$ rm /path/to/foo

分别

$ touch /path/to/bar
$ cat < /path/to/foo > /path/to/bar
$ rm /path/to/foo

根据在编写应用程序期间复制是否到达文件结尾,可能会发生新文件中只有一行的一半的情况。

此外,如果您的应用程序没有关闭并重新打开旧文件,即使看上去已被删除,它也会继续写入旧文件:内核知道哪些文件已打开,尽管它会删除文件系统条目,在应用程序关闭其打开的文件句柄之前,不会删除旧文件的inode和关联的块。


3
仅供参考,早期版本的Unix没有rename()系统调用。因此,原始版本的mv实际上确实调用link()来创建硬链接,然后unlink()删除原始名称。rename()是在FreeBSD中添加的,以在内核中自动实现。
Barmar 2014年

对不起,那是file-system borders什么?
2014年

1
@ laike9m-文件系统边界是指一个简单的文件系统必须驻留在一个存储设备(例如磁盘驱动器)的一个分区上的事实。如果您在文件系统中重命名文件,则所有更改仅是在目录条目中的名称。就像大多数Linux文件系统一样,它仍然具有相同的inode(如果它位于基于inode的文件系统中)。但是,如果将文件移动到另一个文件系统,则必须移动实际数据,并且该文件将从新文件系统获取新的索引节点。这可能会破坏文件操作正在进行的任何操作。
2014年

9

既然您说您正在使用node.js,所以我假设您将使用fs.rename()(或fs.renameSync())重命名文件。该node.js方法已记录为使用rename(2)系统调用,该调用不会以任何方式接触文件本身,而只是更改文件在文件系统中列出的名称:

rename()重命名文件,如果需要,可以在目录之间移动它。对该文件的任何其他硬链接(使用链接(2)创建的)都不会受到影响。oldpath的打开文件描述符也不会受到影响。”

特别是,请注意上面引用的最后一句话,即即使重命名后,所有打开的文件描述符(例如程序将用于写入文件)仍将继续指向该文件。因此,即使文件在同时写入的同时被重命名,也不会丢失或损坏数据。


正如Andreas Weise在他的回答中指出的那样,named(2)系统调用(因此fs.rename()在node.js中)将无法跨越文件系统边界工作。因此,尝试以这种方式将文件移动到其他文件系统将完全失败。

Unix mv命令尝试通过检测错误来隐藏此限制,而是通过将文件的内容复制到新文件并删除原始文件来移动文件。不幸的是,如果在写入文件时移动文件,这样移动文件确实会造成数据丢失的风险。因此,如果要安全地重命名可以同时写入的文件,则不要使用mv(或者至少应确保新路径和旧路径在同一文件系统上)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.