理論的にはそれが可能です。しかし、それは非常に醜く、本質的に手作業でアーカイブを構築する必要があります。
私たちが直面していること
このtar
フォーマットは512バイトのブロックで動作します。このサイズは固定されており、従来のディスクセクターサイズと一致するように設計されています。ファイルをアーカイブに保存する場合、最初の512バイトのブロックは、ファイルメタデータ(名前、サイズ、タイプなど)を含むヘッダーであり、次のブロックにはファイルの内容が含まれます。そのため、アーカイブされたデータは512バイトだけずれてしまいます。
btrfsのブロックサイズ( "--sectorsize")は通常4096バイトです。理論的にはこれを選択できますが、実際にはCPUのページサイズと一致している必要があります。したがって、btrfsのブロックを縮小することはできません。
tar
プログラムは、それが有用であろうように、ほとんど見えブロックサイズの倍数として定義され、より大きな「記録」サイズの概念を持っています。これは、特定のテープドライブのセクターサイズを指定することを目的tar
としているため、部分的なテープレコードの書き込みを回避できます。ただし、データは引き続き512バイト単位で構築およびパックされるため、tar
希望どおりにこれを使用してのブロックを拡張することはできません。
知るために、データの最後のポイントは、ということであるtar
のエンド・オブ・アーカイブマーカーは、これらのブロックは内部のファイルデータである場合を除き、二つの連続オールゼロブロックです。したがって、あらゆる種類の単純なパディングブロックはおそらく受け入れられません。
ハック
私たちにできることは、パディングファイルを挿入することです。アーカイブの先頭で、重複排除したいファイルを追加する前に(それを呼び出すdup
)、次のpad
ようなサイズのファイルを追加します
pad's header + pad's data + dup's header = 4096 bytes.
このように、dup
のデータはブロック境界で始まり、重複排除が可能です。
次に、後続のファイルごとに、前のファイルのサイズを追跡して、正しいパディングを計算する必要があります。ある種のヘッダー拡張が必要になるかどうかも予測する必要があります。たとえば、基本的なtarヘッダーには100バイトのファイルパスしか格納できないため、長いパスは構造的に特別な名前のファイルのデータを使用してエンコードされますフルパス。一般に、ヘッダーサイズの予測には多くの潜在的な複雑さがありtar
ます。ファイル形式には、過去の複数の実装からの多くの欠けがあります。
小さな銀の裏地は、すべてのパディングファイルが同じ名前を共有できることです。そのため、解凍すると、サイズが4096バイト未満の1つの追加ファイルのみが作成されます。
このようなアーカイブを確実に作成する最もクリーンな方法は、おそらくGNU tar
プログラムを変更することです。しかし、CPUとI / Oの時間を犠牲にして迅速かつダーティになりたい場合は、ファイルごとに次のようなことを行うことができます。
#!/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_