Manter (ou restaurar) permissões de arquivo ao substituir o arquivo


11

Eu tenho um comando que aceita um arquivo como argumento, modifica o arquivo e o grava no nome do arquivo especificado no segundo argumento. Vou ligar para esse programa modifyfile.

Eu queria que ele funcionasse "no local", então escrevi um script de shell (bash) que o modifica para um arquivo temporário e depois o move de volta:

TMP=`mktemp`
modifyfile "$original" "$TMP"
mv -v "$TMP" "$original"

Isso tem o efeito colateral lamentável de destruir as permissões nesse arquivo. O arquivo é recriado com permissões padrão.

Existe uma maneira de dizer ao mvcomando para substituir o destino sem alterar suas permissões? Ou, alternativamente, existe uma maneira de salvar o usuário, grupo e permissões do original e restaurá-los?

Respostas:


10

Em vez de usar mv, basta redirecionar cat. Por exemplo:

TMP=$(mktemp)
modifyfile "$original" "$TMP"
cat "$TMP" > "$original"

Isso substitui $originalo conteúdo de $TMP, sem tocar em nada no nível do arquivo.


Isso não seria problemático se algum programa tivesse um identificador de arquivo aberto?
— Martin von Wittich

2
Eu também tenho que rm "$TMP"fazê-lo, mas parece fazer exatamente o que eu quero.
— Stephen Ostermiller

@MartinvonWittich provavelmente seria um problema se você estivesse usando mv. Não vejo uma maneira de resolver esse problema.
— strugee

2
@MartinvonWittich Yes. Create-new-then-move fornece uma alteração atômica e não afeta os programas que têm o arquivo aberto, mas como ele cria um novo arquivo, a propriedade e as permissões do arquivo são perdidas. Truncar existente e gravar preserva as permissões e a propriedade, mas perde dados em caso de falha e passa o tapete sob os pés dos programas que têm o arquivo aberto. Você não pode combinar as partes boas de ambos.
— Gilles 'SO- stop be evil'

1
@MartinvonWittich chownfunciona apenas como raiz. chmode chgrppode ou não funcionar, dependendo das permissões do usuário. Nenhum deles copia outros atributos, como ACL ou atributos estendidos específicos do sistema de arquivos.
— Gilles 'SO- stop be evil'

10

Existem duas estratégias para substituir um arquivo por uma nova versão:

  1. Crie um arquivo temporário com a nova versão e mova-o para o lugar.

    • Vantagem: se um programa abrir esse arquivo, ele lerá o conteúdo antigo ou o novo, dependendo de ter aberto o arquivo antes ou depois da mudança. Não há confusão.
    • Vantagem: em caso de falha, o conteúdo antigo é preservado.
    • Desvantagem: como um novo arquivo é criado, os atributos do arquivo (propriedade, permissão etc.) não são preservados.
  2. Substitua o arquivo antigo no local.

    • Vantagem: os atributos do arquivo são preservados.
    • Desvantagem: em caso de falha, o arquivo pode ser deixado pela metade.
    • Desvantagem: se um programa tem o arquivo aberto quando está sendo atualizado, esse programa pode ler dados inconsistentes.

Se você puder, use o método 1, mas primeiro replique os atributos do arquivo original com cp -p --attributes-only. Isso requer GNU coreutils (ou seja, Linux não incorporado ou ambientes suficientemente semelhantes ao Linux). Se o seu cpnão tiver --attributes-only, omita esta opção: funcionará, mas replicará os dados também.

tmp=$(mktemp)
cp -p --attributes-only "$original" "$tmp"
modifyfile "$original" "$tmp"
mv -f "$tmp" "$original"

Se você não puder replicar os atributos do arquivo existente, por exemplo, porque você possui permissões de gravação, mas não o possui e deseja preservar o proprietário, apenas o método 2 é possível. Para minimizar o risco de perda de dados:

  • Faça com que a janela durante a qual o arquivo fique incompleto seja o menor possível. Prepare os dados primeiro em um arquivo temporário e copie-os no lugar.
  • Faça um backup do arquivo antigo primeiro.

tmp=$(mktemp)
backup="${original}~"
modifyfile "$original" "$tmp"
cp -p "$original" "$backup"
cp -f "$tmp" "$original"

Boa resposta! Atualmente, sugiro usar o argumento --attributes-only com o comando cp no método 1 . Dessa forma, o cp -p --attributes-only "$original" "$tmp"não usará recursos para copiar o conteúdo do arquivo. Não consegui encontrar de qual versão esse argumento foi adicionado.
— Marcelo Barros

@MarceloBarros Foi adicionado no GNU coreutils 8.6 lançado em 15/10/2010, então hoje em dia, se você possui o GNU coreutils, deve tê-lo. Ainda não existe tal coisa com outras cpimplementações.
— Gilles 'SO- stop be evil'

5

Após nossa discussão sobre a primeira resposta, proponho uma resposta diferente:

TMP="$(mktemp "$original".XXXXXXXXXX)"
modifyfile "$original" "$TMP"
chmod --reference="$original" "$TMP"
chown --reference="$original" "$TMP"
mv -f "$TMP" "$original"

Observações:

  • Uso $originalno mktempmodelo para garantir que o arquivo temporário não seja colocado, /tmpmas na mesma pasta que $original. Acredito que, se /tmpmontado em um sistema de arquivos diferente, a operação não seria mais atômica.
  • O resultado de mktempagora é citado caso contenha espaço em branco.
  • Eu uso em $()vez de `` porque eu o considero mais limpo.
  • ch{mod,own} --referencesão usados ​​para transferir as permissões de $originalpara $TMP. Se alguém tiver ideias adicionais sobre quais metadados podem e devem ser transferidos, edite minha postagem e adicione-a.
  • Bem, isso requer permissões de root, como Gilles apontou. Bem, não vou descartar isso agora que escrevi: P
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.