tarファイルの内容が元のファイルのようにブロック整列され、ブロックレベルの重複排除の利点を得るためにtarファイルを生成する方法(https://unix.stackexchange.com/a/208847/9689)?
(私たちがそのような利点を得ないようにするtar形式に本質的なものがないことは正しいですか?それ以外の場合、tar以外の場合、そのような機能が組み込まれた他のアーカイバがありますか?)
PS私は「圧縮されていないtar」を意味しました。 tar+gz または他の非圧縮の tar と質問は、ファイルブロックレベルのソートを可能にするいくつかのトリックを要求しました。 AAFAIRecall tarはテープドライブで使用するように設計されているので、ソートのためにファイル形式に追加のビットを追加することは可能で簡単ですか?それを達成できるツールでもあってほしいです。 ;) 私が覚えている限り、tarファイルはリンクできるので、ソートのためにスペースを埋めるトリックがあるかもしれません。
答え1
理論的には可能です。ただし、これは非常に見苦しく、本質的にアーカイブを手動で構築する必要があります。
私たちが直面する課題
tar
滞在512バイトブロックで実行。サイズは固定されており、既存のディスクセクタのサイズと一致するように設計されています。ファイルがアーカイブに保存されると、最初の512バイトブロックはファイルのメタデータ(名前、サイズ、タイプなど)を含むヘッダーで、後続のブロックにはファイルの内容が含まれます。したがって、アーカイブデータは512バイトだけオフセットされます。
btrfsのブロックサイズ("--sectorsize")通常4096バイト。理論的にはこれを選択できますが、実際にはCPUのページサイズと一致する必要があるようです。したがって、btrfsブロックを縮小することはできません。
このtar
プログラムは、ブロックサイズの倍数として定義されるより大きな「レコード」サイズの概念を持っています。これはほとんど役に立つようです。これはtar
、部分的なテープレコードの書き込みを避けるために、特定のテープドライブのセクタサイズを指定することがわかりました。ただし、データはまだ512バイト単位で構築され圧縮されているため、tar
これを使用して必要に応じてチャンクを増やすことはできません。
知っておくべき最後のデータビットtar
は次のとおりです。アーカイブ終了タグこのブロックがファイルデータ内にない限り、すべてゼロの2つの連続したブロックです。したがって、いかなる種類の素朴なパディングブロックも許容できない可能性がある。
ハッカー攻撃
私たちができることは、フィラーファイルを挿入することです。アーカイブの先頭に重複エントリ(と呼ばれる)を削除するファイルを追加する前に、次のサイズのdup
ファイルを追加します。pad
pad's header + pad's data + dup's header = 4096 bytes.
これにより、dup
データがブロック境界で開始され、重複排除される可能性があります。
次に、各後続のファイルに対して正しいパディングを計算するために、古いファイルのサイズも追跡する必要があります。また、どのようなヘッダー拡張が必要かを予測する必要があります。デフォルトのtarヘッダーファイルパススペースはわずか100バイトで、データがフルパスである構造的に名前付きファイルを使用して長いパスをエンコードします。通常、ヘッダーサイズを予測するには潜在的な複雑さがたくさんあります。tar
ファイル形式には、いくつかの歴史的実装で多くの欠陥がありました。
幸いなことに、すべてのパディングファイルが同じ名前を共有できることです。したがって、解凍すると、サイズが4096バイト未満の追加ファイルのみが残ります。
おそらくそのようなアーカイブを確実に生成する最もきれいな方法は、GNUtar
プログラムを修正することです。ただし、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_