Por que o piping 'tar' em 'dd' não para até que o disco esteja cheio?

18

Eu tenho um arquivo tar de uma única imagem de disco. A imagem dentro deste arquivo tar é de cerca de 4 GB. Eu canalizo a saída de tar xf para dd para gravar a imagem do disco em um cartão SD. O diskdump nunca pára até que o cartão esteja cheio. Aqui está minha sessão de shell:

$ ls -l disk.img.tgz
-rw-r--r-- 1 confus confus 192M Okt  5 00:53

$ tar -tvf disk.img.tgz
-rw-r--r-- root/root 4294968320 2018-10-05 00:52 disk.img

$ lsblk -lb /dev/sdc
NAME MAJ:MIN RM        SIZE RO TYPE MOUNTPOINT
sdc    8:32   1 16022241280  0 disk

$ tar zxf disk.img.tgz -O | sudo dd status=progress conv=sync bs=1M of=/dev/sdc
[sudo] password for user: 
15992881152 bytes (16 GB, 15 GiB) copied, 212 s, 75,4 MB/s 
dd: error writing '/dev/sdc': No space left on device
0+15281 records in
15280+0 records out
16022241280 bytes (16 GB, 15 GiB) copied, 217,67 s, 73,6 MB/s

Por quê? Deve parar após o hit ter escrito a imagem de 4GB para o carrinho de 16GB e nunca ficar sem espaço!

    
por con-f-use 05.10.2018 / 16:15

1 resposta

52

É porque você está fazendo errado.

Você está usando bs=1M , mas a leitura de stdin, pipe, terá leituras menores. De fato, de acordo com o dd, você não conseguiu uma única leitura completa.

E então você tem conv=sync , que complementa as leituras incompletas com zeros.

0+15281 records in
15280+0 records out

dd recebeu 0 completo e 15281 leituras incompletas e escreveu 15280 blocos completos (conv = sync zero filled). Então a saída é muito maior que a entrada, até você não ter mais espaço.

   sync   pad  every  input  block  with  NULs to ibs-size; when used with
          block or unblock, pad with spaces rather than NULs

Para resolver isso, você pode remover conv=sync e adicionar iflag=fullblock .

Para ilustrar, considere yes , que por padrão envia infinito "y \ ny \ ny \ n".

$ yes
y
y
y
^C
$ yes | hexdump -C
00000000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*

Com dd bs=1M conv=sync , é assim:

$ yes | dd bs=1M conv=sync | hexdump -C
00000000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*
0001e000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00100000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*
00112000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*

Portanto, ele obtém um bloco incompleto de "y \ ny \ ny \ n" (0x00000 - 0x1e000, 122880 Bytes) e grava o 1M restante como zeros (0x01e000 - 0x100000, 925696 Bytes). Na maioria dos casos, você não quer que isso aconteça. O resultado é aleatório, assim como você não tem controle real sobre quão incompleta cada leitura se tornaria. Como aqui a segunda leitura não é mais 122880 bytes, mas 73728 bytes.

dd conv=sync raramente é útil e, mesmo nos casos em que seria bem-vindo, como escrever zeros quando você receber erros de leitura, as coisas serão vai terrivelmente errado com isso.

    
por 05.10.2018 / 16:36