Alguns programas de cópia de arquivos gostam rsync
e curl
têm a capacidade de retomar transferências/cópias com falha.
Observando que pode haver muitas causas dessas falhas, em alguns casos o programa pode fazer uma "limpeza" em alguns casos o programa não pode.
Quando esses programas são retomados, eles parecem apenas calcular o tamanho do arquivo/dados que foram transferidos com sucesso e apenas começam a ler o próximo byte da fonte e anexar ao fragmento do arquivo.
por exemplo, o tamanho do fragmento de arquivo que "chegou" ao destino é de 1378 bytes, então eles apenas começam a ler do byte 1379 no original e adicionam ao fragmento.
Minha pergunta é: sabendo que os bytes são compostos de bits e nem todos os arquivos têm seus dados segmentados em blocos limpos de tamanho de byte, como esses programas sabem que o ponto que escolheram para começar a adicionar dados está correto?
Ao escrever o arquivo de destino, ocorre algum tipo de buffer ou "transações" semelhantes aos bancos de dados SQL, no nível do programa, do kernel ou do sistema de arquivos, para garantir que apenas bytes limpos e bem formados cheguem ao dispositivo de bloco subjacente?
Ou os programas assumem que o byte mais recente seria potencialmente incompleto, então eles o excluem supondo que seja ruim, copiam novamente o byte e começam a anexar a partir daí?
sabendo que nem todos os dados são representados como bytes, essas suposições parecem incorretas.
Quando esses programas "retomam", como eles sabem que estão começando no lugar certo?
Para maior clareza - a mecânica real é mais complicada para oferecer uma segurança ainda melhor - você pode imaginar a operação de gravação em disco assim:
Se o processo for interrompido em (1), você não obtém nada no disco, o arquivo está intacto e truncado no bloco anterior. Você enviou 5.000 bytes, apenas 4.096 estão no disco, reinicie a transferência no deslocamento 4.096.
Se em (2), nada acontece exceto na memória. O mesmo que (1). Se em (3), os dados são gravados, mas ninguém se lembra deles . Você enviou 9.000 bytes, 4.096 foram gravados, 4.096 foram gravados e perdidos , o resto foi perdido. A transferência é retomada no deslocamento 4096.
Se em (4), os dados agora devem ter sido confirmados no disco. Os próximos bytes no fluxo podem ser perdidos. Você enviou 9.000 bytes, 8.192 são gravados, o restante é perdido, a transferência é retomada no deslocamento 8.192.
Esta é uma tomada simplificada . Por exemplo, cada gravação "lógica" nos estágios 3-4 não é "atômica", mas dá origem a outra sequência (vamos numerar como #5) em que o bloco, subdividido em sub-blocos adequados para o dispositivo de destino (por exemplo, disco rígido ) é enviado para o controlador host do dispositivo, que também possui um mecanismo de cache , e finalmente armazenado no prato magnético. Essa subsequência nem sempre está totalmente sob o controle do sistema, portanto, enviar dados para o disco rígido não é garantia de que eles foram realmente gravados e poderão ser lidos de volta.
Vários sistemas de arquivos implementam o journaling , para garantir que o ponto mais vulnerável, (4), não seja realmente vulnerável, gravando metadados em, você adivinhou, transações que funcionarão consistentemente, aconteça o que acontecer no estágio (5).
Se o sistema for redefinido no meio de uma transação, ele poderá retomar seu caminho até o ponto de verificação intacto mais próximo. Os dados gravados ainda são perdidos, como no caso (1), mas a retomada cuidará disso. Nenhuma informação realmente se perde.
Observação: não examinei as fontes
rsync
ou qualquer outro utilitário de transferência de arquivos.É trivial escrever um programa C que pule o final de um arquivo e obtenha a posição desse local em bytes.
Ambas as operações são feitas com uma única chamada para a função da biblioteca C padrão
lseek()
(lseek(fd, 0, SEEK_END)
retorna o comprimento do arquivo aberto para o descritor de arquivofd
, medido em bytes).Feito isso para o arquivo de destino, uma chamada semelhante a
lseek()
pode ser feita no arquivo de origem para pular para a posição apropriada:lseek(fd, pos, SEEK_SET)
. A transferência pode continuar nesse ponto, supondo que a parte anterior do arquivo de origem tenha sido identificada como inalterada (utilitários diferentes podem fazer isso de maneiras diferentes).Um arquivo pode estar fragmentado no disco, mas o sistema de arquivos garantirá que um aplicativo perceba o arquivo como uma sequência sequencial de bytes.
Em relação à discussão nos comentários sobre bits e bytes: A menor unidade de dados que pode ser gravada em disco é um byte . Um único byte requer pelo menos um bloco de dados a ser alocado no disco. O tamanho de um bloco depende do tipo de sistema de arquivos e possivelmente também dos parâmetros usados pelo administrador ao inicializar o sistema de arquivos, mas geralmente está entre 512 bytes e 4 KiB. As operações de gravação podem ser armazenadas em buffer pelo kernel, a biblioteca C subjacente ou pelo próprio aplicativo e a gravação real no disco pode ocorrer em múltiplos do tamanho de bloco apropriado como uma otimização.
Não é possível gravar bits únicos em um arquivo e, se uma operação de gravação falhar, ela não deixará "bytes meio escritos" no arquivo.
São basicamente duas questões, porque programas como curl e rsync são muito diferentes.
Para clientes HTTP como curl, eles verificam o tamanho do arquivo atual e enviam um
Content-Range
cabeçalho com a solicitação. O servidor retoma o envio do intervalo do arquivo usando o código de status206
(conteúdo parcial) em vez de200
(sucesso) e o download é retomado ou ignora o cabeçalho e começa do início e o cliente HTTP não tem outra escolha a não ser baixar tudo novamente novamente.Além disso, o servidor pode ou não enviar um
Content-Length
cabeçalho. Você deve ter notado que alguns downloads não estão mostrando uma porcentagem e tamanho do arquivo. Esses são downloads em que o servidor não informa o comprimento ao cliente, portanto, o cliente sabe apenas a quantidade que baixou, mas não quantos bytes seguirão.O uso de um
Content-Range
cabeçalho com posição inicial e final é usado por alguns gerenciadores de download para baixar um arquivo de fontes diferentes de uma só vez, o que acelera a transferência se cada espelho por si só for mais lento que sua conexão de rede.O rsync, por outro lado, é um protocolo avançado para transferências incrementais de arquivos. Ele gera somas de verificação de partes do arquivo no lado do servidor e do cliente para detectar quais bytes são iguais. Aí ele só manda as diferenças. Isso significa que ele não pode apenas retomar um download, mas também pode baixar os bytes alterados se você alterou alguns bytes no meio de um arquivo muito grande sem baixar novamente o arquivo.
Outro protocolo feito para retomar as transferências é o bittorrent, onde o
.torrent
arquivo contém uma lista de somas de verificação para blocos do arquivo, para que os blocos possam ser baixados e verificados em ordem arbitrária e em paralelo de diferentes fontes.Observe que o rsync e o bittorent verificarão os dados parciais em seu disco, enquanto a retomada de um download HTTP não. Então, se você suspeitar que os dados parciais estão corrompidos, você precisa verificar a integridade de outra forma, ou seja, usando uma soma de verificação do arquivo final. Mas apenas interromper o download ou perder a conexão de rede geralmente não corrompe o arquivo parcial, enquanto uma falha de energia durante a transferência pode fazer.
TL;DR: Eles não podem, a menos que o protocolo que eles usam permita.
Os programas nem sempre podem continuar a partir de um local arbitrário: por exemplo, as solicitações HTTP só podem ser reiniciadas se o servidor as suportar e o cliente as implementar: isso não é universal, portanto, verifique a documentação do seu programa. Se o servidor oferecer suporte, os programas podem retomar a transferência simplesmente solicitando como parte do protocolo. Normalmente, você verá transferências parciais em seu diretório de download (elas geralmente são marcadas com uma extensão ".parcial" ou algo semelhante).
Se o download de um arquivo for pausado ou interrompido, o cliente pode gravar o arquivo no disco e ter uma ideia definitiva de onde continuar. Se, por outro lado, o cliente travar ou houver um erro ao gravar no arquivo, o cliente deve assumir que o arquivo está corrompido e começar de novo. O BitTorrent atenua um pouco isso dividindo os arquivos em "pedaços" e acompanhando quais foram baixados com sucesso; o máximo que terá que refazer são alguns pedaços. Rsync faz algo semelhante.
Como os programas sabem que o conteúdo é o mesmo? Um método é verificar se algum identificador é o mesmo entre o cliente e o servidor. Alguns exemplos disso seriam o timestamp e o tamanho, mas existem mecanismos que podem ser específicos de um protocolo. Se os identificadores corresponderem, o cliente pode presumir que a retomada funcionará.
Se você deseja uma verificação mais definitiva, HTTP e amigos não devem ser sua primeira escolha. Você vai querer usar um protocolo que também tenha uma soma de verificação ou hash para todo o arquivo e cada pedaço transferido para que você possa comparar a soma de verificação do download com a soma de verificação do computador do servidor: tudo o que não corresponder será baixado novamente. Novamente, o BitTorrent é um exemplo desse tipo de protocolo; rsync pode, opcionalmente, fazer isso também.
Depende do protocolo usado para transferir. Mas o curl usa http e transfere dados sequencialmente na ordem em que aparecem no arquivo. Portanto, o curl pode ser retomado com base no tamanho do arquivo de uma transferência parcialmente concluída. Na verdade, você pode enganá-lo para pular os primeiros N bytes criando um arquivo de comprimento N (de qualquer coisa) e pedindo-lhe para tratar esse arquivo como um download parcialmente concluído (e então descartar os primeiros N bytes).