O programa deltacp.pl
O programa
deltacp.pl
copia um arquivo (normalmente um dispositivo de bloco) para outra máquina de forma a enviar apenas as diferenças entre eles.
Lista de Discussão
Veja a
lista de discussão do deltacp. Clique
aqui para assiná-la.
Licença e Downloads
Este programa é disponibilizado nos termos da
CC-GNU GPL 2.0.
Sinopse
- Modo originador (ou "gerador de diferenças"):
deltacp.pl opções origem destino
- Modo de recebimento (ou "aplicação das diferenças"):
deltacp.pl opções -O arquivo
origem é um arquivo local, normalmente um dispositivo de bloco tal como
/dev/hdb
ou
/dev/vg0/lv0
.
destino é da forma
usuário@
máquina:
arquivo e aponta para o arquivo de destino na máquina remota.
As
opções podem ser zero ou mais dentre as seguintes:
-v | Modo falante, repita para mais tagarelice |
-b value | Define o tamanho do bloco (65536 por padrão) |
-L filename | Define o nome do arquivo de log de recebimento. Não é gerado se não for especificado. |
-O filename | Coloca o programa em modo de recebimento e especifica o nome do arquivo onde as mudanças serão aplicadas. O arquivo com as diferenças deve vir na entrada padrão. |
-d filename | Salva os blocos originais no arquivo especificado (i.e., cria arquivo de diferenças reversas) |
-D filename | Salva os blocos modificados no arquivo especificado (i.e., cria arquivo de diferenças diretas) |
-x args | Passa os args para o ssh (tal como -C para ativar a compressão ou -o 'algo' ) |
-F | Modo forçado: desativa as checagens de anti-desastre (tal como escrever em um dispositivo que está montado) |
-q | Modo silencioso: sumprime todas as mensagens exceto as de erro |
-n | Modo ensaio: faz quase tudo exceto efetivamente alterar o arquivo de destino (mas gera os diffs) |
-c | Cria o arquivo de saída caso ele não exista |
-e | Apaga os arquivos de diferenças caso eles acabem ficando com 0 bytes |
-t | Truca o arquivo de saída para torná-lo do mesmo tamanho do origial |
-a | Anexa a um arquivo de diferenças já existente |
-s bloco | Especifica o bloco inicial |
-r tentativas | Determina quantas vezes tentar de novo em caso de falha |
-P tempo | Pausa (em segundos) entre as tentativas. Dobrada (até um máximo de 300 segundos) a cada nova tentativa |
Descrição
Modo normal
Entra-se neste modo quando tanto o arquivo de origem quanto o destino são especificados na linha de comando. O programa dispara uma sessão
ssh
para a
máquina remota, logando com o
usuário especificado. Lá ele executará uma outra instância que receberá e aplicará as mudanças geradas pela instância local. Se você omitir tanto
máquina quanto
usuário, o programa operará localmente sem usar o
ssh
.
Ao invocar a outra instância, seja localmente ou por
ssh
, o originador repassa as opções
-D
,
-d
,
-n
,
-e
,
-t
,
-F
and
-b
para a outra instância. Ademais, ele adiciona as opções
-qc
e, é claro,
-O
.
A instância local lê o arquivo de origem bloco por bloco, calcula o hash de cada bloco e os envia para a instância remota. Esta, por sua vez, realiza os mesmos cálculos a partir do arquivo de destino. Se os hashes forem diferentes, ela requisitará os blocos completos. Ao recebê-los, ele os aplicam por sobre o arquivo de destino.
Se a opção
-d
tiver sido especificada, a instância remota salvará os blocos
antes de aplicá-los, criando um outro arquivo binário com as "diferenças reversas" que podem ser usadas para posteriormente reverter a operação.
Se a opção
-D
tiver sido especificada, a instância remota salvará os blocos
após tê-los aplicado, criando um outro arquivo binário com as "diferenças diretas" que podem ser usadas para refazer a operação.
Normalmente o programa não sobrescreve os arquivos de diferenças se eles já existirem, para evitar que os usuários acidentalmente destruam diffs importantes. A opção
--force
pode ser usada para forçar isso.
Pode-se também anexar a um arquivo de diferenças já existente usando a opção
-a
. Isso é útil para continuar uma sessão que tenha sido interrompida.
À medida em que o programa roda, ele mostra estatísticas incluindo quantos blocos foram transferidos até o momento. Se o tamanho do arquivo é conhecido de antemão, será impresso também o número total de blocos e a posição atual como percentagem do total. Se mais de cinco segundos tiverem se passado desde o início da transferência, uma estimativa do tempo restante é mostrada.
Outras estatísticas incluem também quantos blocos foram realmente transferidos por terem havido diferenças e quantos foram pulados por terem sido iguais.
Se o destino é um arquivo comum e for maior que o original, seu tamanho só será truncado para ficar do tamanho do original se a opção
-t
tiver sido explicitamente incluída. Caso contrário, seu tamanho não será alterado.
Se o tamanho do arquivo de origem for maior do que o de destino, duas coisas podem acontecer: se o destino for um dispositivo de bloco, um erro ocorrerá, pois eles não podem aumentar de tamanho (volumes lógicos até podem, mas não automaticamente). Já se o destino for um arquivo comum, ele crescerá para ficar do tamanho do original, desde que o espaço em disco restante assim o permita.
O programa tentará evitar que o usuário faça algo potencialmente destrutivo tal como sobrescrever um sistema de arquivos que está montado ou várias instâncias do programa operando simultaneamente nos mesmos arquivos/dispositivos.
Caso a conexão quebre durante a transferência, o programa se chamará de novo para continuar o envio de onde parou. A cada nova tentativa ele faz uma pausa. Isso é útil quando se está usando o programa sob um link banda larga ou dialup que fica caindo o tempo todo, como em certos hotéis.
Modo de recebimento
Normalmente quem invoca esse modo é a instância originadora, mas pode também ser usado "na mão" para processar os arquivos de diferenças diretas e reversas.
O que define o modo de recebimento é a presença da opção
-O
, que especifica em qual arquivo as alterações serão aplicadas. Nesse caso, a seqüência de comandos de alteração deverá vir da entrada padrão. (É isso que torna o programa ameno a ser rodado via
ssh
pela instância remota).
As opções
-d
e
-D
podem ser usadas para salvar os blocos
antes e
depois da aplicação das alterações, respectvicamente, de forma a gerar os arquivos de diferenças. Se a opção
-e
também tiver sido especificada, quaisquer arquivos de diferenças que acabem ficando vazios são deletados.
Se a opção
-n
estiver presente, o arquivo especificado em
-O
não será modificado. Isso é útil quando você só quer gerar as diferenças sem de fato alterar o arquivo da imagem.
A opção
-t
força o arquivo especificado em
-O
a ficar do mesmo tamanho do arquivo original. Isso funcionará menso que a opção
-n
também esteja presente. O tamanho do arquivo original não apenas é passado no protocolo, mas também fica armazenado nos arquivos de diferenças.
A opção
-t
cria o arquivo especificado em
-O
caso ele não já exista. Sem ela, obtém-se um erro ao especificar um arquivo inexistente.
Exemplos
Nos exemplos abaixo, o que você deve digitar está em negrito. O resto são as respostas dos programas.
Esses exemplos requerem que seu
kernel suporte o dispositivo de
loopback de forma a podermos fingir que um arquivo comum é um dispositivo de bloco. Se seu
kernel não suportar esse recurso, você pode substituir os arquivos
*.img
por algo que aponte para partições reais
extras cujos dados você não se importe em destruir. Contudo, é capaz dos exemplos levarem bem mais tempo porque o tamanho das suas partições provavelmente é bem maior que os meros 16MB que usamos abaixo.
Backup Simples de um Volume
Criemos um arquivo de 16MB totalmente preenchido com bytes zero para servir de espaço para brincarmos ('local$ ' é o prompt normal, sem ser de
root
, na primeira máquina):
local$ dd if=/dev/zero of=example.img bs=16k count=1000
1000+0 records in
1000+0 records out
Façamos o mesmo na máquina remota. Esse espaço será nosso "volume de
backup remoto":
remote$ dd if=/dev/zero of=backup.img bs=16k count=1000
1000+0 records in
1000+0 records out
16384000 bytes (16 MB) copied, 0,584972 seconds, 28,0 MB/s
Criemos um sistema de arquivos ext2 no espaço criado (o resultado do seu
mkfs.ext2
poderá ser um pouco diferente):
local$ yes | /sbin/mkfs.ext2 example.img
mke2fs 1.35 (28-Feb-2004)
example.img is not a block special device.
Proceed anyway? (y,n) Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
4000 inodes, 16000 blocks
800 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=16515072
2 block groups
8192 blocks per group, 8192 fragments per group
2000 inodes per group
Superblock backups stored on blocks:
8193
Writing inode tables: done
Writing superblocks and filesystem accounting information: done
This filesystem will be automatically checked every 31 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
Agora, vamos montá-lo usando o dispositivo de
loopback. Note o
prompt com "#" indicando que você precisa dos poderes de
root
para isso:
local$ /bin/su
Password: (sua senha de root)
local# mkdir /mnt/example
local# mount -t ext2 -o loop example.img /mnt/example
Coloquemos alguma coisa nele:
local# cp /bin/b* /mnt/example
local# ls -l /mnt/example
total 733
-rwxr-xr-x 1 root root 15444 Feb 23 12:02 basename
-rwxr-xr-x 1 root root 616248 Feb 23 12:02 bash
-rwxr-xr-x 1 root root 98356 Feb 23 12:02 bsh
drwx------ 2 root root 12288 Feb 23 12:01 lost+found
No meu exemplo eu tinha apenas três arquivos porque eu estava usando uma máquina com uma instalação minimalista recém terminada. No seu caso provavelmente haverá mais.
Vamos desmontar a imagem de forma que ela fique estática e consistente:
local# umount /mnt/example
local# exit
Agora vamos usar o
deltacp.pl
para copiá-la por sobre o arquivo de backup na outra máquina.
local$ deltacp.pl example.img user@remote:backup.img
current/total current% | skips% sent/skips
250/250 100.0% | 92.8% 18/232 ---d --:--:--
Note que apenas 18 blocos foram realmente transferido dentre o total de 250; evitamos transmitir 92.8% deles (ou seja, 232 blocks). Seus valores exatos muito provavelmente serão diferents, é claro.
Vamos nos convencer de que os dois arquivos são idênticos. Na máquina local, faça:
local$ md5sum example.img
6d7adfd8306c35ff6322a8060e0417f7 example.img
E na remota:
remote$ md5sum backup.img
6d7adfd8306c35ff6322a8060e0417f7 backup.img
Os
hashes batem, entao os arquivos são idênticos. O valor exato dos
seus hashes será diferente, é claro).
Recuperando Arquivos Sobrescritos
Vamos simular um desastre comum: sobrescrever um arquivo importante. Usaremos então o recurso de 'dif reverso' para restaurar o sistema de arquivos a um estado anterior de sorte a podermos acessar o conteúdo do arquivo original antes de ter sido sobrescrito. Por generalidade, esse exemplo usará o
bash
e o
tar
, mas imagine que o
bash
fosse algo muito importante para você, tal como o seu relatório final cujo prazo final é amanhã e cujas muitas notas muitas noites em claro que você investiu nele correm o risco de ir por água abaixo por causa de um instante de distração.
Reusaremos o sistema de arquivos que criamos no exemplo anterior. Primeiro, vamos remontá-lo. Em seguida, vamos simular o desastre, sobrescrevendo o super-importante executável
/bin/bash
. Por fim, desmontaremos o sistema de arquivos.
local% /bin/su
Password: (your root password)
local# mount -t ext2 -o loop example.img /mnt/example
local# cp /bin/tar /mnt/example/bash
cp: overwrite `/mnt/example/bash'? y
local# umount /mnt/example
local# exit
Vamos agora usar o
deltacp.pl
para sincronizá-los. Dessa vez, entretanto, vamos criar um arquivo de diferenças reversas com marcação de data/hora:
local% <b>deltacp.pl -d backup.img-diff-`date +%y%m%d-%H%M%S` example.img user@remote:backup.img</b>
current/total current% | skips% skips/sent
250/250 100.0% | 98.0% 5/245 ---d --:--:--
Agora vamos ver o que obtivemos na máquina remota:
Now, let's see what we have in the remote:
kiko@melee:~$ ls -l backup.img*
-rw-r--r-- 1 kiko kiko 16384000 2007-02-23 12:38 backup.img
-rw-r--r-- 1 kiko kiko 327805 2007-02-23 12:38 backup.img-diff-070223-113928
Tanto atualizamos a imagem quanto também geramos a dif reversa.
Vamos agora montar a imagem de backup no remoto
em modo somente leitura e examinar o programa
bash
:
remote% /bin/su
Password: (your root password)
remote# mkdir /mnt/backup
remote# mount -t ext2 -o ro,loop backup.img /mnt/backup
remote# ls -l /mnt/backup
total 286
-rwxr-xr-x 1 root root 15444 2007-02-23 11:27 basename
-rwxr-xr-x 1 root root 161188 2007-02-23 11:36 bash
-rwxr-xr-x 1 root root 98356 2007-02-23 11:27 bsh
drwx------ 2 root root 12288 2007-02-23 11:27 lost+found
remote# /mnt/backup/bash --version
tar (GNU tar) 1.14
Copyright (C) 2004 Free Software Foundation, Inc.
This program comes with NO WARRANTY, to the extent permitted by law.
You may redistribute it under the terms of the GNU General Public License;
see the file named COPYING for details.
Written by John Gilmore and Jay Fenlason.
Opa! O
bash
não é o
bash
, é o
tar
! Como se a gente não soubesse disso. Mas em uma situação real de desastre, provavelmente ficaríamos surpresos, ou até chocados, em descobrir que nosso precioso arquivo fora sobrescrito -- ou seja, zero chance de ferramentas de "desapagamento" terem sucesso.
Vamos à recuperação: vamos desmontar o sistema de arquivos, aplicar o arquivo de diferenças reversas para fazê-lo "voltar no tempo", remontá-lo e ver no que dá:
remote# umount /mnt/backup
remote# exit
remote$ deltacp.pl -O backup.img < backup.img-rdiff-070223-113928
remote$ /bin/su
Password: (your root password)
remote# mount -t ext2 -o loop backup.img /mnt/backup
remote# /mnt/backup/bash --version
GNU bash, version 3.00.15(1)-release (i386-redhat-linux-gnu)
Copyright (C) 2004 Free Software Foundation, Inc.
remote# umount /mnt/backup
Agora o
bash
é novamente o
bash
.
Coalescência
Suponha que tenhamos vários arquivos de diferenças reversas geradas por nossa rotina de backup diário e queremos voltar a imagem alguns dias no tempo para recuperar algum material deletado. Vamos primeiro dar uma olhada nelas e dar uma olhada no estado da nossa imagem.
local$ ls -l rdiff-07022*
-rw-r--r-- 1 kiko kiko 6490539 2007-02-20 01:29 rdiff-070220
-rw-r--r-- 1 kiko kiko 46220530 2007-02-21 23:37 rdiff-070221
-rw-r--r-- 1 kiko kiko 102275185 2007-02-22 22:20 rdiff-070222
local$ md5sum /dev/sda3
99118e13e6e535bd4c3f5e0df8fbe2ba /dev/sda3
Agora vamos aplicá-las (em ordem reversa, é claro), ao mesmo tempo em que geramos um
arquivo de diferenças reversas coalescidas para que possamos posteriormente voltar ao estado presente.
local$ cat `ls rdiff-07222* | sort -r` | deltacp.pl -d rdiff-coalesced -O /dev/sda2
O arquivo de diferenças coalescidas, contudo, é menor do que a soma das diferenças individuais porque o programa não salva o bloco trocado mais de uma vez:
local$ ls -l rdiff-coalesced
-rw-r--r-- 1 kiko kiko 118009825 2007-02-23 09:11 rdiff-coalesced
Em todo caso, nesse ponto poderíamos montar o sistema de arquivos em
/dev/sda3
, recuperar nosso material, e por aí vai. Ao terminarmos, desmontaríamos o volume e o restauraríamos ao estado anterior usando as diferenças coalescidas:
local$ deltacp.pl -O /dev/sda3 < rdiff-coalesced
-rw-r--r-- 1 kiko kiko 118009825 2007-02-23 09:11 rdiff-coalesced
local$ md5sum /dev/sda3
99118e13e6e535bd4c3f5e0df8fbe2ba /dev/sda3
local$ rm rdiff-coalesced
Motivação
O
rsync
é um programa de backup/espelhamento diferencial cheio de recursos que faz muito e muito mais do que o
deltacp.pl
faz -- mas ele não lê nem escreve em dispositivos de bloco: ele reclama com a mensagem 'skipping non-regular file' se você tentar.
Folheando pelos arquivos da lista de discussão do
rsync
, descobri que essa questão
vez por outra é levantada. Foi proposto até um
patch para isso, mas é para uma versão 2.5.5 antigona, sem muito das features e atualizações de seguranças recentes. O consenso na lista parece ser que esse recurso é mais indicado para um utilitário próprio para isso e por isso o
rsync
não o suportará.
O Protocolo
O arquivo é dividido em blocos de igual tamanho, exceto possivelmente o último, se o tamanho do arquivo não for um múltiplo do tamanho do bloco.
Para cada bloco, o originador transmite uma mensagem de 25 bytes:
- Comando: 1 byte
- Número do bloco: 4 bytes, como um inteiro longo big-endian
- Comprimento do bloco: 4 bytes, como um inteiro longo big-endian
- Hash do bloco: 16 bytes, usando o algoritmo MD5.
O destinatário computa o hash do bloco especificado e o compara com o recebido. Se baterem, ele responde 0x0, significando "não me mande o bloco, pois ele é igual ao que eu tenho". Caso contrário, ele responde com 0x1, significando "por favor me envie esse bloco, pois ele difere do que eu tenho"
Se o originador receber um 0x1, ele manda o bloco completo. Caso contrário, ele passa direto para o próximo bloco. O processo é repetido até se chegar ao fim do arquivo.
Sobrecarga
Se os arquivos de origem e destino forem totalmente diferentes, o programa adiciona exatamente
25*int((file_size+block_size-1)/block_size)
bytes extras. Isso dá cerca de 15MB para cada 40GB de dados -- meros 0,038%, se usarmos o tamanho padrão dos blocos.
Isso significa que esses 15MB serão transferidos mesmo que nada tenha mudado.
Características Dinâmicas
Sendo um protocolo tipo "pare-e-aguarde", sua eficiência é dominada pela latência: funciona melhor em redes locais onde a latêntica tipicamente é menor que um milissegundo.
Por outro lado, o tamanho default dos blocos, sendo bem maior do que os pacotes TCP, dissipa um pouco esse efeito em
links com latências de várias dezenas de milissegundos, como é o caso da Internet ou VPNs.
Prerequisitos e Instalação
O programa requer
Perl 5, preferivelmente com suporte a arquivos grandes (>2GB).
Para checar se seu Perl satisfaz esse requisito, digite:
perl -V | grep -ic USE_LARGE_FILES
.
Se você obtiver 1, seu Perl quase certamente é adequado. Se você obtiver 0, o
deltacp.pl
ainda funcionará, mas só conseguirá tratar arquivos de até 2GB.
Por padrão o programa usa seu cliente SSH: se você conseguir conectar na máquina remota via
ssh
, o
deltacp.pl
também o conseguirá.
Para instalar, descompacte-o no
/usr/local/bin
e defina as permissões para 755. Certifique-se de que este diretório está listado em algum lugar da variável de ambiente
PATH
.
topo