É "quebrado" para substituir um arquivo existente sem fsync ()?

3

Na página mount(2) man do Linux, notei o seguinte trecho:

Many broken applications don't use fsync() when replacing existing files via patterns such as

fd = open("foo.new")/write(fd,...)/close(fd)/ rename("foo.new", "foo")

or worse yet

fd = open("foo", O_TRUNC)/write(fd,...)/close(fd).

If auto_da_alloc is enabled, ext4 will detect the replace-via-rename and replace-via-truncate patterns and force that any delayed allocation blocks are allocated such that at the next journal commit, in the default data=ordered mode, the data blocks of the new file are forced to disk before the rename() operation is committed. This provides roughly the same level of guarantees as ext3, and avoids the "zero-length" problem that can happen when a system crashes before the delayed allocation blocks are forced to disk.

Em que sentido este código está "quebrado"? Eles estão dizendo que esse código é ilegal ou não está em conformidade com o padrão (POSIX, etc)?

Obviamente, fsync() pode ser uma boa ideia para pessoas que estão preocupadas com o que aconteceria se o sistema falhasse. Mas, assumindo que um sistema não falha, as duas versões do código de exemplo, sem fsync() , fazem exatamente a coisa certa?

    
por Nate Eldredge 22.07.2016 / 18:43

3 respostas

4
Espera-se que

rename seja atômico: seja totalmente concluído ou não seja totalmente. Renomear A para tomar o lugar de B deve deixá-lo com A e B intactos (isso não aconteceu); ou com apenas o conteúdo de A sob o nome B (completou completamente).

Contanto que o sistema não trave, isso acontecerá independentemente das chamadas fsync (etc.).

Se o sistema falhar, no entanto, pode acontecer que o próprio nome mude para o disco (e, portanto, seja completado). Lembre-se que esses nomes! = Arquivos. Arquivos / inodes podem ter vários nomes. Renomear está mudando os nomes, não os arquivos / dados subjacentes.

Assim, você pode ter o estado em que seu programa escreveu A, renomeá-lo para substituir B e, em seguida, a energia acabou. Acontece que o sistema de arquivos escreveu a renomeação para o disco, mas não os dados reais em A. Não é necessário sem fsync . Assim, você acaba com um B de comprimento zero ou um B com zero.

A razão pela qual um aplicativo faz um arquivo write-temp + renomear em vez de simplesmente sobrescrever o arquivo é porque ele quer segurança contra falhas. O usuário não ficará muito zangado se uma cópia temporária de seu importante documento for deixada por aí, ao lado da cópia em bom estado. Mas se não houver boas cópias, o usuário não ficará satisfeito.

    
por 22.07.2016 / 19:45
2

O código é legal, mas "ingênuo". O problema é exatamente o que acontece durante um acidente

Há um risco potencial de que os novos dados não tenham espaço alocado para ele antes que o diretório seja atualizado e, portanto, corre o risco de perda de dados.

Um bom aplicativo chamará fflush() e fsync() para garantir que os dados sejam liberados para o disco. As rotinas auto_da_alloc são uma tentativa de fazer isso heuristicamente no kernel.

O

link explica algumas das "dicas".

    
por 22.07.2016 / 18:57
0

Se for perfeitamente legal, funcionará, mas não fará o que você quer.

O segundo é óbvio. Destrói o original antes de salvar o novo.

O primeiro é menos óbvio, parece que se houve uma falha no sistema (por exemplo, falta de energia), então você não teria feito nada (não iniciado); tem 2 arquivos: antigos e novos; ou bem sucedido. No entanto, este não é o caso, a menos que você faça o que diz fsync .

Eles estão usando a palavra quebrado para chamar sua atenção. Está quebrado, pois seus usuários perderão dados. Talvez não hoje. Talvez não amanhã, mas logo e pelo resto da vida.

Isso é o que é chamado de um bug intermitente: um bug que pode permanecer por anos antes de exibir sintomas.

Se você não se importa com a integridade dos dados de seus usuários, por que fazer o primeiro exemplo.

    
por 22.07.2016 / 18:50