Atomicamente escreve um arquivo sem alterar inodes (preservar hard link)

3

A maneira normal de escrever um arquivo X com segurança no Unix é:

  1. Grave o novo conteúdo do arquivo em um arquivo temporário Y .
  2. rename(2) Y a X

Em duas etapas, parece que não fizemos nada além de alterar X "no local".

Está protegido contra condições de corrida e perda de dados não intencionais (em que X é destruído, mas Y está incompleto ou destruído).

A desvantagem (neste caso) é que ele não grava o inode referido por X in-place; rename(2) faz com que X se refira a um novo número de inode.

Quando X era um arquivo com contagem de links > 1 (um link rígido explícito), agora ele não se refere ao mesmo inode de antes, o link físico está quebrado.

A maneira óbvia de eliminar a desvantagem é gravar o arquivo no local, mas isso não é atômico, pode falhar, pode resultar em perda de dados, etc.

Existe alguma maneira de fazer isso atomicamente, como rename(2) , mas preservar links físicos?

Talvez para alterar o número de inode de Y (o arquivo temporário) para o mesmo que X e atribuir o nome X ? Um nível de inode "renomear".

Isso efetivamente escreveria o inode referido por X com o novo conteúdo de Y , mas não quebraria sua propriedade de link físico e manteria o nome antigo.

Se o inode hipotético "rename" fosse atômico, então eu acho que isso seria atômico e protegido contra perda de dados / raças.

    
por cat 29.10.2018 / 00:02

1 resposta

3

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:

  1. Abrindo o arquivo para obter um descritor de arquivo.
  2. opcional procure o offset de gravação desejado
  3. Escrevendo no arquivo.
  4. 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 ?

    
por 02.11.2018 / 16:53