Como o `dd` pode ser usado para deslocar os blocos de dados à direita?

10

Considere um dispositivo de bloco bruto de 100 MB como um exemplo simples. Ou seja, 204800 blocos de 512 bytes cada para um total de 102760448 bytes.

O desafio é mudar os primeiros 98 MB (200704 blocos) para que haja uma diferença de 2 MB (4096 blocos) à sua frente. Fazer isso no local requer que nada seja gravado em um setor que não foi lido. Uma maneira de conseguir isso é introduzir um buffer:

$ dd if=/dev/sdj2 count=200704 | mbuffer -s 512 -b 4096 -P 100 | dd of=/dev/sdj2 seek=4096

A expectativa é que mbufferele armazene 4096 blocos antes de passar qualquer coisa para o gravador, garantindo assim que nada seja gravado em uma área que não tenha sido lida e que o gravador fique atrasado no tamanho do buffer. O buffer deve permitir que o leitor e o gravador operem o mais rápido possível dentro desses constrangimentos.

No entanto, parece não funcionar de maneira confiável. Eu tentei usar dispositivos reais, mas nunca funciona neles, enquanto as experiências com um arquivo funcionavam na minha caixa de 64 bits, mas não na minha caixa de 32 bits.

Primeiro, alguma preparação:

$ dd if=/dev/sdj2 count=200704 | md5sum
0f0727f6644dac7a6ec60ea98ffc6da9
$ dd if=/dev/sdj2 count=200704 of=testfile

Isso não funciona:

$ dd if=/dev/sdj2 count=200704 | mbuffer -s 512 -b 4096 -P 100 -H | dd of=/dev/sdj2 seek=4096
summary: 98.0 MiByte in  4.4sec - average of 22.0 MiB/s
md5 hash: 3cbf1ca59a250d19573285458e320ade

Isso funciona no sistema de 64 bits, mas não no sistema de 32 bits:

$ dd if=testfile count=200704 | mbuffer -s 512 -b 4096 -P 100 -H | dd of=testfile seek=4096 conv=notrunc
summary: 98.0 MiByte in  0.9sec - average of  111 MiB/s
md5 hash: 0f0727f6644dac7a6ec60ea98ffc6da9

Como isso pode ser feito de maneira confiável?


notas

Eu li outras perguntas sobre buffering e olhei pv, buffere mbuffer. Só consegui que o último funcionasse com o tamanho de buffer necessário.

Usar o armazenamento intermediário é uma solução óbvia para o problema que sempre funciona, mas não é prático quando não há capacidade disponível suficiente disponível.

Plataformas de teste executando o Arch Linux com a mbufferversão 20140302.

starfry
fonte
Não acho que isso resolveria o problema, mas por curiosidade, por que usar mbuffer? Por que não fazer a ddleitura de todo o conteúdo do dispositivo de bloco de uma só vez dd bs=102760448? Obviamente, de uma forma ou de outra, é armazenado em buffer na RAM.
Celada
@ Celada - o exemplo de 100 MB foi apenas um exemplo. Ler 1 TB, por exemplo, de uma só vez não seria uma boa ideia.
starfry
2
Ah, eu entendo agora, obrigado. O mbufferfato deve forçar o segundo dda ficar para trás em primeiro lugar e você só precisa de RAM suficiente para armazenar em buffer o tamanho do turno. Pena ddque não suporta blocos de leitura e gravação em ordem inversa, pois isso eliminaria o problema!
Celada
Você não lista como você calculou o md5sum segunda
psusi
@psusi, o segundo md5 é produzido pelo mbuffer (seu -Hargumento ativa esse recurso).
starfry

Respostas:

2

Sem um buffer, você poderia voltar atrás, um bloco de cada vez.

for i in $(seq 100 -1 0)
do
    dd if=/dev/thing of=/dev/thing \
       bs=1M skip=$i seek=$(($i+2)) count=1
done

Observe que este exemplo é perigoso devido à falta de verificação de erros.

Também é lento devido à quantidade de ddchamadas. Se você tiver memória de sobra, poderá usar um tamanho de bloco maior.

Com um buffer, cuidado com as armadilhas . É não suficiente para garantir uma Prefill 100%. O que você precisa é de um preenchimento mínimo durante todo o processo. O buffer nunca deve cair abaixo, 2Mpois, caso contrário, você substituirá os dados ainda a serem lidos novamente.

Portanto, enquanto na teoria você poderia fazer sem qualquer tipo de buffer e apenas encadear dd:

dd if=/dev/thing bs=1M | \
dd bs=1M iflag=fullblock | \
dd bs=1M iflag=fullblock | \
dd of=/dev/thing bs=1M seek=2

Na prática, isso não funciona de maneira confiável, porque não há garantia de que o primeiro ddconsiga continuar lendo os dados, enquanto o último dd(com 2M"buffer" no meio) já está gravando.

Você pode aumentar suas chances consideravelmente, tornando o buffer intermediário consideravelmente maior, mas, mesmo assim, não é confiável.

Infelizmente, não conheço um bom programa de buffer com propriedade mínima de preenchimento. Você precisa de um que interrompa a saída desde que haja menos do que sua margem de segurança no buffer.

frostschutz
fonte
Aceitei isso porque ele responde à pergunta original, demonstrando como ddpoderia ser usado. Penso, no entanto, que a solução real não é usar, ddmas sim optar por algo que é projetado para rodar para trás ddrescue. Eu descrevi uma maneira de fazer isso em uma resposta.
starfry
1
@ starfry: claro, um programa que apenas faz isso será uma boa solução. No entanto, não tenho a certeza ddrescueaqui. Não, se espera trabalhar em diferentes dispositivos, e você deve enganá-lo para aceitar seus argumentos. Também pode não ter a propriedade "mínimo de preenchimento do buffer" internamente (já que em dispositivos diferentes não é necessário), portanto, novamente, pode corromper seus dados. Você precisaria verificar o código-fonte se ele foi realmente projetado para o seu caso de uso.
Frostschutz
1

Você está lendo 4096 blocos e, em seguida, gravando esses 4096 blocos nos próximos 4096 blocos do disco, substituindo, assim, os segundos 4096 blocos antes que possam ser lidos. Você precisa ler 8129 blocos para obter esses segundos 4096 antes de iniciar qualquer gravação e, em seguida, só precisa escrever 4096 blocos antes de ler os próximos 4096.

Você não mencionou que tipo de sistema de arquivos é esse. Se for ext [234] e você tiver uma versão recente do e2fsprogs, poderá usar e2image -ra -O 512 /dev/sdj2. Isso também tem o benefício adicional de ser inteligente o suficiente para pular o espaço livre no volume.

psusi
fonte
Isso faz sentido ao lê-lo e vou dar outra olhada com base nisso. Mas isso não explica por que funcionou no arquivo de teste.
starfry
No sistema de arquivos, você está se referindo ao sistema de arquivos que contém o meu arquivo de teste? Aquela ext4mas para a cópia dispositivo de bloco, qualquer sistema de arquivos deve ser irrelevante.
starfry
@starfry, a única maneira que conheço de fazer isso de maneira genérica é usar o algoritmo sugerido por Emmanuel (trabalhar de trás para frente a partir do final), que é o que o gparted faz.
Psusi
re o tamanho do bloco, eu tinha tentado blocos maiores (eu deveria ter escrito isso na pergunta). Eu descobri que ele não se tornou mais confiável, mesmo um buffer do setor de 64K. A solução confiável é correr para trás, algo que ddnão funciona.
starfry
1

Uma solução confiável exige que você garanta que nada seja gravado em uma área que pode não ter sido lida e a única maneira real de conseguir isso é executar a cópia na direção inversa.

A ddrescueferramenta pode trabalhar na direção inversa, mas se recusa a executar com a entrada e a saída iguais. No entanto, é possível enganá-lo duplicando o nó do dispositivo.

Realizei algumas experiências rápidas e parece funcionar. A linha de comando é:

$ ddrescue -f -R -s 200704s -o 4096s /dev/sdj11 /dev/sdj11_copy

Os argumentos são

  • -f é necessário para forçá-lo a gravar em um dispositivo de saída existente
  • -R diz para ele trabalhar na direção inversa
  • -sinforma quanto da entrada a copiar (usei o ssufixo para especificar o número de setores)
  • -odiz para procurar no dispositivo de saída antes de escrever (especificado em setores novamente com o ssufixo)
  • /dev/sdj11 é o dispositivo de bloco para ler
  • /dev/sdj11_copy é o dispositivo de bloco para escrever

Eu criei /dev/sdj11_copycom mknodpara combinar com os parâmetros de /dev/sdj11.

Eu fiz apenas alguns testes muito rápidos, mas isso parece funcionar bem para copiar um dispositivo bruto. Ele não funciona em um arquivo (eu não poderia induzi-lo a ir além dos arquivos iguais)

Isso não responde à minha pergunta original, que perguntou como conseguir isso, ddmas acho que, depois de ler as outras respostas, a resposta é que ddnão é possível.

starfry
fonte
O que acontece se ddrescuedescobrir um bloco defeituoso nesse cenário? Se ele pular para outra área do disco (para evitar blocos defeituosos) e continuar copiando a partir daí, substituirá novamente partes ainda não copiadas dos seus dados. Se ele não espera trabalhar com o mesmo dispositivo, não há motivos para tomar medidas especiais para evitar vários possíveis casos de corrupção de dados.
Frostschutz
Concordo que esse é um problema em potencial, mas não olhei para os casos extremos, pois pude usá-lo para fazer o que precisava. Existem ddrescueopções para limitar suas tentativas de recuperar dados incorretos, mas não tentei usá-los.
starfry
O fato de ele se recusar a operar se a entrada e a saída forem as mesmas provavelmente é uma boa indicação de que não é seguro.
Psd #