Isso pode ser feito, em teoria. Mas é muito feio e envolve essencialmente a construção manual do nosso arquivo.
O que estamos enfrentando
O formato tar
funciona em blocos de 512 bytes . Esse tamanho é fixo e destina-se a corresponder ao tamanho do setor de disco tradicional. Ao armazenar um arquivo em um arquivo, o primeiro bloco de 512 bytes é um cabeçalho que contém metadados de arquivo (nome, tamanho, tipo etc.) e os seguintes blocos contêm o conteúdo do arquivo. Portanto, nossos dados arquivados serão desalinhados em 512 bytes.
O tamanho do bloco ("--sectorize") do btrfs é normalmente de 4096 bytes . Em teoria, podemos escolher isso, mas, na prática, parece que tem que coincidir com o tamanho da página da nossa CPU. Portanto, não podemos diminuir os bloqueios do btrfs.
O programa tar
tem o conceito de um tamanho maior de "registro", definido como um múltiplo do tamanho do bloco, que quase parece que seria útil. Acontece que isso significa especificar o tamanho do setor de uma determinada unidade de fita, para que tar
evite gravar registros parciais de fita. No entanto, os dados ainda são construídos e compactados em unidades de 512 bytes, portanto, não podemos usar isso para aumentar os blocos de tar
como você esperava.
Um último ponto de dados a saber é que o final de do tar
O marcador de arquivamento é dois blocos de todos os zeros consecutivos, exceto quando esses blocos estão dentro dos dados do arquivo. Portanto, qualquer tipo de bloco de preenchimento ingênuo provavelmente não será aceito.
O Hack
O que podemos fazer é inserir arquivos de preenchimento. No início do nosso arquivo, antes de adicionarmos o arquivo que queremos desduplicar (chamemos dup
), adicionamos um arquivo pad
, dimensionado para que
pad's header + pad's data + dup's header = 4096 bytes.
Dessa forma, os dados de dup
começam em um limite de bloco e podem ser desduplicados.
Em seguida, para cada arquivo subseqüente, também precisamos acompanhar o tamanho do arquivo anterior para calcular o preenchimento correto. Também temos que prever se algum tipo de extensão de cabeçalho será necessário: por exemplo, o o cabeçalho de alcatrão básico só tem espaço para 100 bytes de caminho de arquivo, então caminhos mais longos são codificados usando o que é estruturalmente um arquivo especialmente nomeado cujos dados são o caminho completo. Em geral, há muita complexidade em potencial para prever o tamanho do cabeçalho - o formato de arquivo tar
tem muita sujeira de várias implementações históricas.
Um pequeno forro de prata é que todos os arquivos de preenchimento podem compartilhar o mesmo nome, então, quando nós desarmarmos, apenas teremos um único arquivo extra com menos de 4096 bytes de tamanho.
A maneira mais limpa de criar um arquivo confiável como este é provavelmente modificar o programa GNU tar
. Mas se você quiser ser rápido e sujo às custas da CPU e do tempo de E / S, você pode, para cada arquivo, fazer algo como:
#!/bin/bash
# Proof of concept and probably buggy.
# If I ever find this script in a production environment,
# I don't know whether I'll laugh or cry.
my_file="$2"
my_archive="$1"
file_size="$(wc -c <"$my_file")"
arch_size="$(tar cb 1 "$my_file" | wc -c)" # "b 1": Remember that record size I mentioned? Set it to equal the block size so we can measure usefully.
end_marker_size=1024 # End-of-archive marker: 2 blocks' worth of 0 bytes
hdr_size="$(( (arch_size - file_size - end_marker_size) % 4096 ))"
pad_size="$(( (4096 - 512 - hdr_size) % 4096 ))"
(( pad_size < 512 )) && pad_size="$(( pad_size + 4096 ))"
# Assume the pre-existing archive is already a multiple of 4096 bytes long
# (not including the end-of-archive marker), and add extra padding to the end
# so that it stays that way.
file_blocks_size="$(( ((file_size+511) / 512) * 512 ))"
end_pad_size="$(( 4096 - 512 - (file_blocks_size % 4096) ))"
(( end_pad_size < 512 )) && end_pad_size="$(( end_pad_size + 4096 ))"
head -c $pad_size /dev/zero > _PADDING_
tar rf "$my_archive" _PADDING_ "$my_file"
head -c $end_pad_size /dev/zero > _PADDING_
tar rf "$my_archive" _PADDING_