Documentation index

アーカイブのメタデータ(Archive metadata)

ほとんどのアーカイブ形式は、特別に指定しない限り、ビルド環境に由来する情報をメタデータとして記録するようになっています。 わかりやすいのはファイルの最終更新日時ですが、ファイルの並び順、ユーザー、グループ、(数値の)ID、権限も気になるところです。 このセクションでは説明用に Tar 形式を使用しますが、他の形式についてもだいたい同じことが言えると思います。

ファイル変更日時

ほとんどのアーカイブ形式ではファイルの最終変更日時と作成日時を記録します。

Tar では指定した変更日時を全てのファイルに設定できます。

$ tar --mtime='2015-10-21 00:00Z' -cf product.tar build

(日時文字列末尾の Zタイムゾーン が UTC であることを示しています)

Tar 以外のアーカイブ形式では、アーカイブを作成する前に touch コマンドで 任意の変更日時 を設定できます。

$ find build -print0 |
    xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}"
$ zip -r product.zip build

ビルドプロセスで作成、変更しないファイルについては元の日時を残すほうが望ましいでしょう。

$ find build -newermt "@${SOURCE_DATE_EPOCH}" -print0 |
    xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}"
$ zip -r product.zip build

GNU Tar について後者の場合を簡単に扱うための修正パッチが作成されています。 Debian の tar についてはバージョン 1.28-1 から利用できるようになりました。 少し待てば upstream に反映されるでしょうけど、使うときは注意が必要です。 新しいフラグ --clamp-mtime を指定すると、--mtime で指定した日時より変更日時が新しいファイルの日時を変更するようになります。

# Only in Debian unstable for now
$ tar --mtime='2015-10-21 00:00Z' --clamp-mtime -cf product.tar build

元から存在するファイルの変更日時を変更したくないときは便利な機能です。

ファイルの並び順

たいていのアーカイブ形式では、ディレクトリの内容を読み取るとき、ファイルの並び順はファイルシステムと同じようになります。 つまり、実行するたびに変化する場合があります

GNU Tar では、バージョン 1.28 で導入した --sort=name フラグを使用すると、ロケールに依存せずファイルを並び替えることができるようになりました。

# Works with GNU Tar 1.28
$ tar --sort=name -cf product.tar build

それより古いバージョン、あるいは他のアーカイブ形式では、find コマンドと sort コマンドを組み合わせれば同じ結果を生成できます。

$ find build -print0 | LC_ALL=C sort -z |
    tar --no-recursion --null -T - -cf product.tar

sort コマンドを実行するとき、意図しない照合順序で並び替えが行われないよう、ロケールを C にするのを忘れないようにしましょう。

ユーザー、グループ、(数値の)ID

アーカイブ形式によってはファイル所有者のユーザーやグループを記録できます。 文字列の場合もありますし、対応する(数値の)IDの場合もあります。

既定のシステムグループに所属するファイルなら何も問題はありません。 しかし、ほとんどの場合ビルドを実行するのは一般ユーザーです。 アカウント名や関連するIDを記録してしまうと、ビルドの再現性を損ねてしまう場合があります。

Tar ではファイル所有者のユーザーやグループを指定できます。 ユーザーとグループに 0 を指定して --numeric-owner も一緒に指定するのが安全です。 そうすれば確実に 0 を記録するからです。

$ tar --owner=0 --group=0 --numeric-owner -cf product.tar build

PAX ヘッダ

GNU Tar で PAX 形式を使用するときに環境変数 POSIXLY_CORRECT を設定すると、ファイルの作成日時、アクセス日時、tar プロセスのプロセスIDを非決定論的に決まるメタデータとして追加するようになります。

この振る舞いを止めるには、POSIXLY_CORRECT を設定しないか(tar 1.32 より新しいバージョン が必要です)、tar の引数に --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime--format=gnu を追加するか(どちらも GNU Tar で使用できるフラグです)、特に問題なければ --format=usatr を使用します。

完全な例

Tar コマンドでアーカイブを作成するときのお手本です。

# requires GNU Tar 1.28+
$ tar --sort=name \
      --mtime="@${SOURCE_DATE_EPOCH}" \
      --owner=0 --group=0 --numeric-owner \
      --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime \
      -cf product.tar build

後処理

再現性のあるアーカイブを作成できないツールでも、後処理でどうにかなる場合があります。

strip-nondeterminism は Zip や Jar アーカイブの正規化に対応しています(制限 はあります)。 Tor Browser の使用するスクリプト re-dzip.sh のような方法もあるでしょう。

静的ライブラリ

UNIX 風システムでは、ar というアーカイブ形式の静的ライブラリ(.a ファイル)を使用します。 他のアーカイブ形式と同様に、タイムスタンプやユーザーID、グループID、ファイル権限をメタデータとして記録しています。 どれもライブラリとして使用するときは不要な情報です。

GNU Ar や binutils 由来のツールは、ユーザーID、グループID、タイムスタンプを 0 にして全てのファイルを同じ種類として扱う deterministic mode に対応しています。 ./configure の引数に --enable-deterministic-archives を指定すると、有効になります。 ディストリビューションによっては有効化された状態で配布しているし[distros-with-default]、Makefile のターゲットが archive.a(foo.o) になっている場合を除けば安全だと言えるでしょう(関連する不具合)。

binutils を --enable-deterministic-archives でビルドしていない場合、ビルドシステムは ar や関連するコマンドに適切な引数を渡さないといけません。 たいていのビルドシステムでは環境変数 ARFLAGSDcvr を設定すれば、deterministic mode を有効化できます。 関数索引を作成するために ranlib を使っているときは -D を渡すのを忘れないようにしてください。

後処理の方法として、strip-nondeterminism を使う方法や、objcopy を使う方法があります。

    objcopy --enable-deterministic-archives libfoo.a

前の実行方法では ファイルの並び順 を固定できません。

Initramfs イメージ

initramfs イメージは cpio 形式のアーカイブです。 cpio ヘッダー形式(詳しくは man 5 cpio してください)にはデバイスやiノード番号を記録できますが、これらはシステムによって異なります。

これらの情報を除外する方法として、bsdtar をパイプで連結する方法があります。

決定論的な結果にならない例です。

echo ucode.bin |
    bsdcpio -o -H newc -R 0:0 > ucode.img

決定論的な結果になる例です。

echo ucode.bin |
    bsdtar --uid 0 --gid 0 -cnf - -T - |
    bsdtar --null -cf - --format=newc @- > ucode.img

タイムスタンプなどの問題も解決する必要があるので注意してください。

GNU Libtool

コミットハッシュが 74c8993c 以前(最初に取り込まれたバージョンは 2.2.7b)の GNU Libtool では、find コマンドの結果を並び替えていませんでした。 そういう状態で作成された(ブートストラップされた)パッケージは大量に存在します。

GNU GCCltmain.sh は libtool 2.2.7a が生成した、と報告するのですが、GNU GCC は独自に libtoo.m4ltmain.sh を保守しており、再現性の問題については d41cd173e23 で解消済みになっています。 この修正が取り込まれた最初のバージョンは 9.1.0 なので、それより前のバージョンの GCC にはビルドの再現性を損ねる可能性が残っていると言えます。


Documentation index

Follow us on Twitter @ReproBuilds, Mastodon @reproducible_builds@fosstodon.org & Reddit and please consider making a donation. • Content licensed under CC BY-SA 4.0, style licensed under MIT. Templates and styles based on the Tor Styleguide. Logos and trademarks belong to their respective owners. • Patches welcome via our Git repository (instructions) or via our mailing list. • Full contact info