Se puede hacer, en teoría. Pero es muy feo y esencialmente implica construir nuestro archivo a mano.
A lo que nos enfrentamos
El tar
formato funciona en bloques de 512 bytes . Este tamaño es fijo y está destinado a coincidir con el tamaño tradicional del sector del disco. Al almacenar un archivo en un archivo, el primer bloque de 512 bytes es un encabezado que contiene metadatos del archivo (nombre, tamaño, tipo, etc.), y los siguientes bloques contienen el contenido del archivo. Entonces nuestros datos archivados estarán desalineados por 512 bytes.
El tamaño de bloque ("--sectorsize") de btrfs es típicamente 4096 bytes . En teoría podemos elegir esto, pero en la práctica parece que tiene que coincidir con el tamaño de página de nuestra CPU. Entonces no podemos reducir los bloques de btrfs.
El tar
programa tiene un concepto de un tamaño de "registro" más grande, definido como un múltiplo del tamaño de bloque, que casi parece que sería útil. Resulta que esto está destinado a especificar el tamaño del sector de una unidad de cinta dada, para tar
evitar escribir registros de cinta parciales. Sin embargo, los datos aún se construyen y empaquetan en unidades de 512 bytes, por lo que no podemos usar esto para aumentar tar
los bloques como esperaba.
Un último punto de datos para saber es que tar
el marcador de fin de archivo es dos bloques consecutivos de todos ceros, excepto cuando esos bloques están dentro de los datos del archivo. Por lo tanto, es probable que no se acepte ningún tipo de relleno ingenuo.
El truco
Lo que podemos hacer es insertar archivos de relleno. Al comienzo de nuestro archivo, antes de agregar el archivo que queremos deduplicar (llamarlo dup
), agregamos un archivo pad
, dimensionado para que
pad's header + pad's data + dup's header = 4096 bytes.
De esa manera, dup
los datos comienzan en un límite de bloque y pueden deduplicarse.
Luego, para cada archivo posterior, también tenemos que hacer un seguimiento del tamaño del archivo anterior para calcular el relleno correcto. También tenemos que predecir si se necesitará algún tipo de extensión de encabezado: por ejemplo, el encabezado tar básico solo tiene espacio para 100 bytes de ruta de archivo, por lo que las rutas más largas se codifican utilizando lo que estructuralmente es un archivo especialmente nombrado cuyos datos son El camino completo. En general, existe una gran complejidad potencial en la predicción del tamaño del encabezado: el tar
formato del archivo tiene muchas complicaciones de múltiples implementaciones históricas.
Un pequeño resquicio de esperanza es que todos los archivos de relleno pueden compartir el mismo nombre, por lo que cuando descomprimimos solo terminaremos con un solo archivo adicional de menos de 4096 bytes de tamaño.
La forma más limpia de crear de manera confiable un archivo como este es probablemente modificar el tar
programa GNU . Pero si desea ser rápido y sucio a expensas de la CPU y el tiempo de E / S, puede, para cada archivo, hacer 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_