Это определит несжатый размер потока 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 сжатых байта за раз. Таким образом, вы можете использовать gzip.py, если вас устраивает использование памяти до 1056768 байт для несжатых данных (1032 * 1024, где 1032: 1 - максимальная степень сжатия deflate). В решении здесь используется 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 ГБ нулей до 722 байтов. Таким образом, вы не можете измерить выход bz2.decompress
, измерив вход, как это можно сделать с zlib.decompress
, даже без второго аргумента. Отсутствие ограничения на размер распакованного вывода является фундаментальным недостатком интерфейса Python.
Я просмотрел _bz2module.c в 3.3, чтобы узнать, есть ли недокументированный способ его использования, чтобы избежать этой проблемы. Нет никакого способа обойти это. Функция decompress
просто продолжает увеличивать буфер результатов до тех пор, пока не сможет распаковать весь предоставленный ввод. _bz2module.c необходимо исправить.
person
Mark Adler
schedule
23.12.2012