这将确定 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
与第二个参数一起使用,它限制了未压缩数据的数量。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
函数不提供第二个参数zlib.decompress
。更糟糕的是,由于运行长度编码,bz2 格式的最大压缩率比 zlib 高得多。bzip2 将 1 GB 的零压缩为 722 字节。因此,即使没有第二个参数,您也无法bz2.decompress
通过测量输入来测量输出。zlib.decompress
对解压后的输出大小没有限制是 Python 接口的一个根本缺陷。
我查看了 3.3 中的 _bz2module.c 以查看是否有未记录的方式来使用它来避免此问题。没有其他办法了。那里的decompress
函数只是不断增长结果缓冲区,直到它可以解压缩所有提供的输入。_bz2module.c 需要修复。