Это можно сделать теоретически. Но это очень уродливо и по сути включает в себя создание нашего архива вручную.
Что мы против
tar
Формат работает на 512-байтовых блоках . Этот размер является фиксированным и предназначен для соответствия традиционному размеру сектора диска. При сохранении файла в архиве первый 512-байтовый блок является заголовком, который содержит метаданные файла (имя, размер, тип и т. Д.), А следующие блоки содержат содержимое файла. Таким образом, наши архивные данные будут смещены на 512 байт.
Размер блока ("--sectorsize") btrfs обычно составляет 4096 байт . Теоретически мы можем выбрать это, но на практике это выглядит так, как будто оно должно соответствовать размеру страницы нашего процессора. Поэтому мы не можем уменьшить блоки btrfs.
tar
Программа имеет концепцию большего «рекорд» размер, определяемый как кратное размера блока, который выглядит почти как это было бы полезно. Оказывается, это предназначено для указания размера сектора данного ленточного накопителя, чтобы tar
избежать частичной записи на ленту. Однако данные по-прежнему создаются и упаковываются в блоки по 512 байт, поэтому мы не можем использовать это для увеличения tar
блоков, как вы и надеялись.
Последний пункт , данные , чтобы знать, что tar
«s конец из-архива маркер состоит из двух последовательных всех нулей блоков, кроме случаев , когда эти блоки находятся внутри файл данные. Так что любые наивные блоки заполнения, вероятно, не будут приняты.
Взломать
Что мы можем сделать, это вставить файлы отступов. В начале нашего архива, прежде чем мы добавим файл, который мы хотим дедуплицировать (назовем его dup
), мы добавим файл pad
, имеющий такой размер, чтобы
pad's header + pad's data + dup's header = 4096 bytes.
Таким образом, dup
данные начинаются с границы блока и могут быть дедуплицированы.
Затем для каждого последующего файла мы также должны отслеживать размер предыдущего файла, чтобы вычислить правильный отступ. Мы также должны предсказать, понадобится ли какое-то расширение заголовка: например, в базовом заголовке tar есть место только для 100 байтов пути к файлу, поэтому более длинные пути кодируются с использованием того, что структурно является файлом со специальным именем, данные которого полный путь. В общем, существует много потенциальных сложностей в прогнозировании размера заголовка - tar
формат файла имеет много различий от нескольких исторических реализаций.
Небольшая серебряная подкладка состоит в том, что все файлы заполнения могут иметь одно и то же имя, поэтому, когда мы распаковываем, мы получим только один дополнительный файл размером менее 4096 байт.
Самый чистый способ надежного создания такого архива - это, вероятно, модифицировать программу GNU tar
. Но если вы хотите быть быстрым и грязным за счет процессорного времени и времени ввода-вывода, вы можете для каждого файла сделать что-то вроде:
#!/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_