Isso pode ser feito, em teoria. Mas é muito feio e envolve essencialmente construir nosso arquivo manualmente.
O que estamos enfrentando
O tar
formato opera em blocos de 512 bytes . Esse tamanho é fixo e se destina a corresponder ao tamanho tradicional do setor de disco. Ao armazenar um arquivo em um arquivo morto, o primeiro bloco de 512 bytes é um cabeçalho que contém os metadados do 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 ("--sectorsize") dos btrfs é tipicamente 4096 bytes . Em teoria, podemos escolher isso, mas, na prática, parece que ele deve corresponder ao tamanho da página da nossa CPU. Portanto, não podemos reduzir os bloqueios de btrfs.
O tar
programa tem um conceito de um tamanho maior de "registro", definido como um múltiplo do tamanho do bloco, o que quase parece que seria útil. Acontece que isso visa especificar o tamanho do setor de uma determinada unidade de fita, para tar
evitar a gravação de registros parciais. No entanto, os dados ainda são construídos e compactados em unidades de 512 bytes, portanto, não podemos usá-lo para aumentar tar
os blocos como você esperava.
Um último ponto de dados a saber é que tar
o marcador de fim de arquivamento é de dois blocos consecutivos com todos os zeros, 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.
The Hack
O que podemos fazer é inserir arquivos de preenchimento. No início do nosso arquivo, antes de adicionarmos o arquivo que queremos deduplicar (chame-o dup
), adicionamos um arquivo pad
dimensionado para que
pad's header + pad's data + dup's header = 4096 bytes.
Dessa forma, dup
os dados começam em um limite de bloco e podem ser deduplicados.
Em seguida, para cada arquivo subsequente, também precisamos acompanhar o tamanho do arquivo anterior para calcular o preenchimento correto. Também temos que prever se será necessário algum tipo de extensão de cabeçalho: por exemplo, o cabeçalho tar básico só tem espaço para 100 bytes de caminho do arquivo; portanto, caminhos mais longos são codificados usando o que é estruturalmente um arquivo especialmente nomeado cujos dados são o caminho completo. Em geral, existe muita complexidade em potencial para prever o tamanho do cabeçalho - o tar
formato do arquivo tem muito problema de várias implementações históricas.
Um pequeno revestimento prateado é que todos os arquivos de preenchimento podem compartilhar o mesmo nome; portanto, quando desarmarmos, teremos apenas um único arquivo extra com menos de 4096 bytes de tamanho.
A maneira mais limpa de criar um arquivo confiável como esse é provavelmente modificar o tar
programa GNU . Mas se você quiser ser rápido e sujo às custas do tempo de CPU e E / S, poderá, 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_