É seguro mover um arquivo que está sendo anexado?

28

Eu tenho um processo node.js que usa fs.appendFile para adicionar linhas a file.log . Apenas linhas completas de cerca de 40 caracteres por linhas são anexadas, por ex. as chamadas são como fs.appendFile("start-end") , não duas chamadas como fs.appendFile("start-") e fs.appendFile("end") . Se eu mover este arquivo para file2.log , posso ter certeza de que nenhuma linha foi perdida ou parcialmente copiada?

    
por Fluffy 28.10.2014 / 11:25

2 respostas

36

Contanto que você não mova o arquivo pelas bordas do sistema de arquivos, a operação deve ser segura. Isso se deve ao mecanismo, como a “movimentação” é realmente feita.

Se você mv um arquivo no mesmo sistema de arquivos, o arquivo não é realmente tocado, mas apenas a entrada do sistema de arquivos é alterada.

$ mv foo bar

na verdade faz algo como

$ ln foo bar
$ rm foo

Isso criaria um link hard (uma segunda entrada de diretório) para o arquivo (na verdade, o inode apontado pela entrada do sistema de arquivos) foo named bar e removeria a entrada foo . Desde agora, ao remover foo , há uma segunda entrada no sistema de arquivos apontando para o inode de foo , removendo a entrada antiga foo , na verdade, não remove nenhum bloco pertencente ao inode.

Seu programa seria feliz para anexar ao arquivo de qualquer maneira, desde o seu identificador de arquivo aberto aponta para o inode do arquivo, não a entrada do sistema de arquivos.

Nota: Se o seu programa fechar e reabrir o arquivo entre as gravações, você acabaria tendo um novo arquivo criado com a entrada do sistema de arquivos antigo!

Movimentos entre sistemas de arquivos cruzados:

Se você mover o arquivo pelas bordas do sistema de arquivos, as coisas ficarão feias. Nesse caso, você não poderia garantir a consistência do arquivo, já que mv na verdade

  • crie um novo arquivo no sistema de arquivos de destino
  • copie o conteúdo do arquivo antigo para o novo arquivo
  • remova o arquivo antigo

ou

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

resp.

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

Dependendo se a cópia chegar ao final do arquivo durante a gravação do seu aplicativo, pode acontecer de você ter apenas metade de uma linha no novo arquivo.

Além disso, se seu aplicativo não fechar e reabrir o arquivo antigo, ele continuará gravando no arquivo antigo, mesmo que pareça ser excluído: o kernel sabe quais arquivos estão abertos e, embora ele exclua o sistema de arquivos entrada, ele não excluirá o inode do arquivo antigo e os blocos associados até que seu aplicativo feche o identificador de arquivos aberto.

    
por 28.10.2014 / 11:54
9

Como você diz que está usando node.js, presumo que esteja usando fs.rename() (ou fs.renameSync() ) para renomear os arquivos. Esse método node.js é documentado para usar a chamada de sistema rename (2) , que não toca no próprio arquivo de qualquer forma, mas apenas altera o nome sob o qual ele está listado no sistema de arquivos:

"rename() renames a file, moving it between directories if required. Any other hard links to the file (as created using link(2)) are unaffected. Open file descriptors for oldpath are also unaffected."

Em particular, observe a última frase citada acima, que diz que qualquer descritor de arquivo aberto (como seu programa estaria usando para gravar no arquivo) continuará apontando para ele mesmo depois de ter sido renomeado. Assim, não haverá perda ou corrupção de dados, mesmo que o arquivo seja renomeado enquanto estiver sendo gravado simultaneamente.

Como Andreas Weise observa na sua resposta , a chamada ao sistema rename (2) (e, portanto, fs.rename() no nó .js) não funcionará nos limites do sistema de arquivos. Assim, tentar mover um arquivo para um sistema de arquivos diferente dessa maneira simplesmente falhará.

O comando mv do Unix tenta ocultar essa limitação detectando o erro e, em vez disso, movendo o arquivo copiando seu conteúdo para um novo arquivo e excluindo o original. Infelizmente, mover arquivos como este ameaça a perda de dados se o arquivo for movido enquanto estiver sendo gravado. Assim, se você quiser renomear com segurança os arquivos que podem ser gravados simultaneamente, você deve não usar mv (ou, pelo menos, você deve ter certeza absoluta de que o caminho novo e antigo esteja no caminho). mesmo sistema de arquivos).

    
por 28.10.2014 / 20:07