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

10

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

O desafio é deslocar os primeiros 98MB (200704 blocos) para que haja uma lacuna de 2MB (4096 blocos) na frente dele. Para fazer isso no local, é necessário que nada seja gravado em um setor que não tenha sido 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 mbuffer 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 atrás do leitor pelo tamanho do buffer. O buffer deve permitir que o leitor e o gravador funcionem o mais rápido possível dentro dessas restrições.

No entanto, não parece funcionar de forma confiável. Eu tentei usar dispositivos reais, mas ele nunca funciona neles, enquanto experimentos com um arquivo funcionaram 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 forma confiável?

notas

Li outras perguntas sobre o armazenamento em buffer e observei pv , buffer e mbuffer . Eu só consegui que o último trabalhasse com o tamanho do buffer necessário.

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

Plataformas de teste executando o Arch Linux com mbuffer versão 20140302.

    
por starfry 13.11.2014 / 13:30

3 respostas

2

Sem um buffer, você poderia voltar para trá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

Por favor, note que este exemplo é perigoso devido à falta de verificação de erros.

Também é lento devido à quantidade de dd de chamadas. Se você tem memória de sobra, você poderia usar um tamanho maior de blocos.

Com um buffer, cuidado com armadilhas . Não é suficiente para garantir um pré-enchimento de 100%. O que você precisa é de um preenchimento mínimo durante todo o processo. O buffer nunca deve cair abaixo de 2M , porque senão você terá sobrescrito seus dados ainda a serem lidos novamente.

Então, em teoria, você poderia fazer sem qualquer tipo de buffer e apenas cadeia 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 forma confiável porque não há garantia de que o primeiro dd consiga manter os dados de leitura, enquanto o último dd (com 2M de "buffer" entre ) já está escrevendo.

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

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

    
por 14.11.2014 / 17:06
1

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

A ferramenta ddrescue pode funcionar em uma direção inversa, mas se recusa a executar com a entrada e a saída sendo as mesmas. No entanto, é possível enganá-lo duplicando o nó do dispositivo.

Eu realizei alguns experimentos rápidos 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 trabalhar em uma direção reversa
  • -s informa quanto da entrada copiar (usei o sufixo s para especificar o número de setores)
  • -o diz para buscar para frente no dispositivo de saída antes de gravar (especificado em setores novamente com o sufixo s )
  • /dev/sdj11 é o dispositivo de bloco para ler
  • /dev/sdj11_copy é o dispositivo de bloco para gravar

Eu criei /dev/sdj11_copy com mknod para corresponder aos parâmetros de /dev/sdj11 .

Eu fiz apenas alguns testes muito rápidos, mas parece que isso funciona bem para copiar um dispositivo bruto. Ele não funciona em um arquivo (eu não consegui ir além dos arquivos sendo os mesmos)

Isso não responde à minha pergunta original que perguntou como alcançar isso com dd , mas acho que, depois de ler as outras respostas, a resposta é que dd não pode fazer isso.

    
por 14.11.2014 / 21:55
0

Você está lendo 4096 blocos e, em seguida, grava esses 4096 blocos nos próximos 4096 blocos do disco, sobrescrevendo os segundos 4096 blocos antes que possam ser lidos. Você precisa ler 8129 blocos para obter os segundos 4096 antes de começar a escrever, e então você precisa escrever apenas 4096 blocos antes de ler o próximo 4096.

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

    
por 14.11.2014 / 17:30