これにより、限られたメモリを使用しながら、gzip ストリームの圧縮されていないサイズが決まります。
#!/usr/bin/python
import sys
import zlib
f = open(sys.argv[1], "rb")
z = zlib.decompressobj(15+16)
total = 0
while True:
buf = z.unconsumed_tail
if buf == "":
buf = f.read(1024)
if buf == "":
break
got = z.decompress(buf, 4096)
if got == "":
break
total += len(got)
print total
if z.unused_data != "" or f.read(1024) != "":
print "warning: more input after end of gzip stream"
tar ファイル内のすべてのファイルを抽出すると、必要なスペースが少し過大に見積もられます。長さには、これらのファイルと tar ディレクトリ情報が含まれます。
gzip.py コードは、入力データのサイズを除いて、解凍されるデータの量を制御しません。gzip.py では、圧縮された 1024 バイトを一度に読み取ります。したがって、圧縮されていないデータのメモリ使用量が最大約 1056768 バイト (1032 * 1024、1032:1 は deflate の最大圧縮率) であれば、gzip.py を使用できます。ここでのソリューションではzlib.decompress
、圧縮されていないデータの量を制限する 2 番目の引数を使用します。gzip.py はありません。
これにより、tar 形式をデコードすることにより、抽出された tar エントリの合計サイズが正確に決定されます。
#!/usr/bin/python
import sys
import zlib
def decompn(f, z, n):
"""Return n uncompressed bytes, or fewer if at the end of the compressed
stream. This only decompresses as much as necessary, in order to
avoid excessive memory usage for highly compressed input.
"""
blk = ""
while len(blk) < n:
buf = z.unconsumed_tail
if buf == "":
buf = f.read(1024)
got = z.decompress(buf, n - len(blk))
blk += got
if got == "":
break
return blk
f = open(sys.argv[1], "rb")
z = zlib.decompressobj(15+16)
total = 0
left = 0
while True:
blk = decompn(f, z, 512)
if len(blk) < 512:
break
if left == 0:
if blk == "\0"*512:
continue
if blk[156] in ["1", "2", "3", "4", "5", "6"]:
continue
if blk[124] == 0x80:
size = 0
for i in range(125, 136):
size <<= 8
size += blk[i]
else:
size = int(blk[124:136].split()[0].split("\0")[0], 8)
if blk[156] not in ["x", "g", "X", "L", "K"]:
total += size
left = (size + 511) // 512
else:
left -= 1
print total
if blk != "":
print "warning: partial final block"
if left != 0:
print "warning: tar file ended in the middle of an entry"
if z.unused_data != "" or f.read(1024) != "":
print "warning: more input after end of gzip stream"
これの変形を使用して、爆弾の tar ファイルをスキャンできます。これには、データを解凍する前にヘッダー情報で大きなサイズを見つけることができるという利点があります。
.tar.bz2 アーカイブに関しては、Python bz2 ライブラリ (少なくとも 3.3 の時点) は、大量のメモリを消費する bz2 爆弾に対して不可避的に安全ではありません。このbz2.decompress
関数は、2 番目の引数を提供していませんzlib.decompress
。これは、ランレングス コーディングにより、bz2 形式の最大圧縮率が zlib よりもはるかに高いという事実によってさらに悪化します。bzip2 は、1 GB のゼロを 722 バイトに圧縮します。したがって、2 番目の引数がなくてもbz2.decompress
実行できるように、入力を計測して の出力を計測することはできません。zlib.decompress
解凍後の出力サイズに制限がないことは、Python インターフェイスの根本的な欠陥です。
3.3 の _bz2module.c を調べて、この問題を回避するための文書化されていない使用方法があるかどうかを確認しました。それを回避する方法はありません。そこdecompress
にある関数は、提供されたすべての入力を解凍できるまで、結果バッファーを拡大し続けます。_bz2module.c を修正する必要があります。