A questão
Você tem uma lista (exaustiva) de chamadas de sistemas aqui .
Você notará que não há uma chamada "substituir o conteúdo deste inode". A modificação desse conteúdo sempre implica:
- Abrindo o arquivo para obter um descritor de arquivo.
- opcional procure o offset de gravação desejado
- Escrevendo no arquivo.
- opcional Truncando dados antigos, se novos dados forem menores.
O passo 4 pode ser feito mais cedo. Existem alguns atalhos também, como pwrite , que escrevem diretamente em um deslocamento especificado , combinando as etapas 2 e 3 ou espalhe a redação .
Uma maneira alternativa é usar um mapeamento de memória , mas fica ainda pior cada byte escrito pode ser enviado para o arquivo subjacente de forma independente (conceitualmente, como se cada gravação fosse uma chamada de 1 byte write
).
→ O ponto é que o melhor cenário que você pode ter ainda é 2 operações: uma write
e uma truncate
.
Qualquer que seja a ordem em que você os executa, você ainda arrisca um outro processo para mexer com o arquivo no meio e acabar com um arquivo corrompido.
Soluções
Solução normal
Como você observou, é por isso que a abordagem canônica é criar um novo arquivo, você sabe que é o único escritor de (você pode até garantir isso combinando O_TMPFILE
e linkat
), redirecione atomicamente o nome antigo para o novo arquivo.
Existem duas outras opções, mas ambas falham de alguma forma:
Bloqueio obrigatório
Permite que o acesso a arquivos seja negado a outros processos, definindo uma combinação especial de sinalizadores. Soa como a ferramenta para o trabalho, certo? No entanto:
- Ele deve estar ativado no nível do sistema de arquivos (é um sinalizador durante a montagem).
Warning: the Linux implementation of mandatory locking is unreliable.
Since Linux 4.5, mandatory locking has been made an optional feature. This is an initial step toward removing this feature completely.
Isso é apenas lógico, já que o Unix sempre evitou bloqueios. Eles são propensos a erros e é impossível cobrir todos os casos de borda e garantir que não haja impasse.
Bloqueio de consultoria
Ele é definido usando a chamada de sistema fcntl . No entanto, é apenas consultivo e a maioria dos programas simplesmente o ignora.
Na verdade, é bom apenas para gerenciar bloqueios no arquivo compartilhado entre vários processos que cooperam.
Conclusão
Is there some way to do it atomically like rename(2) but preserve hard links?
Não.
Os inodes são de baixo nível, quase um detalhe de implementação. Muito poucas APIs reconhecem sua existência (acredito que a família de chamadas stat
seja a única).
Qualquer coisa que você tente fazer provavelmente depende do uso indevido do design dos sistemas de arquivos Unix ou simplesmente pedir muito a ele.
Isso poderia ser um problema XY ?