彷徨えるフジワラ

年がら年中さまよってます

ZIP 形式アーカイブのタイムスタンプ問題

※ 2012/09/13 追記

とりあえず、時間の無い人向けに、簡単にまとめると:

#!/bin/sh
# USAGE: hgziparch ZIPFILE [OPTS]

ZIPFILE=$1; shift

# 一時ディレクトリを作成
mkdir /tmp/$$
# tar 形式アーカイブを元に展開イメージを作成
hg archive -t tar $* - | (cd /tmp/$$ && tar xf - )
# 展開イメージを元に zip アーカイブを作成
(cd /tmp/$$ && zip -rq - *) > ${ZIPFILE}
# 展開イメージを破棄
rm -rf /tmp/$$

ちなみに、この問題の根本的な原因は、Python が標準提供している zip アーカイブ取り扱いライブラリの zipfile モジュールが、タイムスタンプに関する extended attribute を扱えないことにあるので、Mercurial が独自に対応するのは難しい見込み。既存機能の範囲で修正可能なことが判明しました! ※ 2012/09/13 追記

以上、簡単なまとめ終わり。

以下は、この障害に関する事の顛末。
発端は Twitter で寄せられた以下の不具合情報。

『9時間』というあたりが、明らかにタイムゾーン絡みなんだろうなぁ、とおもわせる問題なので、再現性確認と、問題箇所の絞込みを行う事に。

"hg archive -t zip -r REV" で生成した ZIP アーカイブを、Windowsエクスプローラにより展開したところ、何のひねりも無く再現。

実装を確認したところ、アーカイブファイル作成処理では、tar も zip も格納されるタイムスタンプ情報はGMT ベースのものが使用されている。で、tar で展開した場合はタイムスタンプの問題が無いことから、unzip の実装がよろしくないのでは?という印象。

ところが、BTS での障害報告ベースでやり取りする過程で、zip アーカイブに extended attribute としてタイムスタンプ情報が付いている場合は、期待通りにタイムスタンプが設定されるらしいという情報を入手。

確かに、zip コマンドの通常実行で作成したアーカイブは、期待通りのタイムスタンプで展開されるけれど、"zip -X" 実行により extended attribute 無しで作成されたアーカイブは、"hg archive -t zip" と同様に想定外のタイムスタンプで展開される。

それならば、アーカイブファイル作成の際に extended attribute を足してやれば良かろう、と思いきや、Python 標準の zipfile モジュールでは、ZIP アーカイブの extended attribute に関する配慮が無いことが判明…… orz

こうなると、Python 本体に修正提案 ⇒ 取り込みができたとしても、古い Python 処理系では問題が解消されないので、根本的に対処しようとするなら、実行時にクラス定義を上書きしてやる、などの対処が必要に。

うーん、ZIP ファイルの仕様を把握するだけで、相当に骨が折れそうなので、そこまで頑張る気力がなぁ………。

※ 以下、2012/09/13 追記

と思っていたら、既存機能の範囲で修正可能であるとの情報が!

(extended, extra) x (attribute, field) の組み合わせが散在していて、ZIP の仕様書を読み込む気力が全然無かっただけに、この情報は大変ありがたい。

技術的な目処は付いたから、後は修正/試験だけだな。