10 ポイント 投稿者 xguru 2022-07-25 | 1件のコメント | WhatsAppで共有
  • 518GiB の tar.gz を展開すると極端に遅くなるため、調査を開始
  • Tar フォーマットの説明と、高速な tar extractor のコードを書きながら解説

オリジナルの Tar ファイルフォーマット

  • Tar はアーカイブファイルとしては非常に異例
    → アーカイブヘッダーがなく、検索用のファイルインデックスもなく、tar であることを確認できるマジックバイトもなく、フッターもなく、メタデータもない
    → Tar の中にあるのは、ファイルオブジェクトという 1 種類だけ
  • ファイルタイプ: 0(通常ファイル)、1(ハードリンク)、2(シンボリックリンク)
  • 512 バイトのファイルオブジェクトヘッダーの構造を説明
    → 最大の制限はファイルパス長が 100 文字しかないこと。また、ファイルサイズは最大 8GiB

UStar 拡張ファイル

  • ファイルパス長は最大 256 で、新しいファイルタイプも追加
  • ヘッダーを拡張し、マジックバイトと prefix フィールドを追加
  • ファイルタイプ追加: 3(キャラクターデバイス)、4(ブロックデバイス)、5(ディレクトリ)、6(FIFO ファイル)、7(連続ファイル)
  • ただし 8GiB のサイズ制限はそのまま

Pax ファイルフォーマット

  • POSIX.1-2001 標準として、pax CLI を通じて tar フォーマットを拡張
  • UStar と同じだが、x と g のファイルフォーマットを追加
    → 拡張ヘッダーレコードとして、x は次のファイルにのみ適用され、g は後続のすべてのファイルに適用

GNU Tar ファイルフォーマット

  • 独自フォーマットは gnu で、pax とは異なる
  • pax に似ており UStar ベースだが、パスや大きなファイルをエンコードする別の方法を使う
    L タイプ: 次のファイルオブジェクトのペイロードが "file_path" を表す
    K タイプ: 次のファイルオブジェクトのペイロードが "link_path" を表す
    → この 2 つは連続して適用可能
    → ファイルが 8GiB より大きい場合は file_size の先頭文字のハイビットをセットし、その後は文字列の残りを base 256 として解析(95 ビット整数)

なぜ GNU tar は展開が遅いのか?

  • ファイルヘッダーに file_path="../hello.txt" のような値を入れるとセキュリティ問題が発生する。しかし防ぐのは簡単ではない
  • GNU tar は link_path に .. を見つけるとプレースホルダーを作成し、遅延して処理する
  • しかし .. のないハードリンクの場合は直接作成したくても、すでにプレースホルダーがあるため作成できない
  • つまりハードリンクを作成するには、それが遅延リンクかどうかをまずすべて確認する必要があり、もしそうなら新しいリンクもやはり遅延処理しなければならない
    → すべてのハードリンクについて遅延リンクを探索する必要がある。理由は不明だが、実際には 2 回探索している
  • 筆者の Tar ファイルには .. を含むリンクが 80 万件超、ハードリンクが 540 万件以上あり、そのため展開が遅くなっていた
  • これを防ぐには tar に --absolute-paths または -P オプションを追加すること
    → 絶対パスを保存し、.. を拒否するオプション
    → つまり -P オプションを付けると遅延リンク機構が無効になる