Merged revisions 83959-83960 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/branches/py3k

........
  r83959 | antoine.pitrou | 2010-08-12 17:11:50 +0200 (jeu., 12 août 2010) | 5 lines

  Issue #7467: when a file from a ZIP archive, its CRC is checked and a
  BadZipfile error is raised if it doesn't match (as used to be the
  case in Python 2.5 and earlier).
........
  r83960 | antoine.pitrou | 2010-08-12 17:15:01 +0200 (jeu., 12 août 2010) | 3 lines

  Typo.
........
This commit is contained in:
Antoine Pitrou 2010-08-12 15:25:51 +00:00
parent 3523443f77
commit e1436d1092
3 changed files with 107 additions and 2 deletions

View File

@ -5,6 +5,7 @@ except ImportError:
zlib = None zlib = None
import os import os
import io
import sys import sys
import time import time
import shutil import shutil
@ -653,6 +654,27 @@ class PyZipFileTests(unittest.TestCase):
class OtherTests(unittest.TestCase): class OtherTests(unittest.TestCase):
zips_with_bad_crc = {
zipfile.ZIP_STORED: (
b'PK\003\004\024\0\0\0\0\0 \213\212;:r'
b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af'
b'ilehello,AworldP'
b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:'
b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0'
b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi'
b'lePK\005\006\0\0\0\0\001\0\001\0003\000'
b'\0\0/\0\0\0\0\0'),
zipfile.ZIP_DEFLATED: (
b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA'
b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0'
b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n'
b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00'
b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00'
b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00'),
}
def test_unicode_filenames(self): def test_unicode_filenames(self):
with zipfile.ZipFile(TESTFN, "w") as zf: with zipfile.ZipFile(TESTFN, "w") as zf:
zf.writestr(u"foo.txt", "Test for unicode filename") zf.writestr(u"foo.txt", "Test for unicode filename")
@ -864,6 +886,49 @@ class OtherTests(unittest.TestCase):
with zipfile.ZipFile(TESTFN, mode="r") as zipf: with zipfile.ZipFile(TESTFN, mode="r") as zipf:
self.assertEqual(zipf.comment, comment2) self.assertEqual(zipf.comment, comment2)
def check_testzip_with_bad_crc(self, compression):
"""Tests that files with bad CRCs return their name from testzip."""
zipdata = self.zips_with_bad_crc[compression]
with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
# testzip returns the name of the first corrupt file, or None
self.assertEqual('afile', zipf.testzip())
def test_testzip_with_bad_crc_stored(self):
self.check_testzip_with_bad_crc(zipfile.ZIP_STORED)
@skipUnless(zlib, "requires zlib")
def test_testzip_with_bad_crc_deflated(self):
self.check_testzip_with_bad_crc(zipfile.ZIP_DEFLATED)
def check_read_with_bad_crc(self, compression):
"""Tests that files with bad CRCs raise a BadZipfile exception when read."""
zipdata = self.zips_with_bad_crc[compression]
# Using ZipFile.read()
with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
self.assertRaises(zipfile.BadZipfile, zipf.read, 'afile')
# Using ZipExtFile.read()
with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
with zipf.open('afile', 'r') as corrupt_file:
self.assertRaises(zipfile.BadZipfile, corrupt_file.read)
# Same with small reads (in order to exercise the buffering logic)
with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
with zipf.open('afile', 'r') as corrupt_file:
corrupt_file.MIN_READ_SIZE = 2
with self.assertRaises(zipfile.BadZipfile):
while corrupt_file.read(2):
pass
def test_read_with_bad_crc_stored(self):
self.check_read_with_bad_crc(zipfile.ZIP_STORED)
@skipUnless(zlib, "requires zlib")
def test_read_with_bad_crc_deflated(self):
self.check_read_with_bad_crc(zipfile.ZIP_DEFLATED)
def tearDown(self): def tearDown(self):
unlink(TESTFN) unlink(TESTFN)
unlink(TESTFN2) unlink(TESTFN2)
@ -963,6 +1028,11 @@ class TestsWithRandomBinaryFiles(unittest.TestCase):
for f in (TESTFN2, TemporaryFile(), StringIO()): for f in (TESTFN2, TemporaryFile(), StringIO()):
self.zip_test(f, zipfile.ZIP_STORED) self.zip_test(f, zipfile.ZIP_STORED)
@skipUnless(zlib, "requires zlib")
def test_deflated(self):
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
self.zip_test(f, zipfile.ZIP_DEFLATED)
def zip_open_test(self, f, compression): def zip_open_test(self, f, compression):
self.make_test_archive(f, compression) self.make_test_archive(f, compression)
@ -996,6 +1066,11 @@ class TestsWithRandomBinaryFiles(unittest.TestCase):
for f in (TESTFN2, TemporaryFile(), StringIO()): for f in (TESTFN2, TemporaryFile(), StringIO()):
self.zip_open_test(f, zipfile.ZIP_STORED) self.zip_open_test(f, zipfile.ZIP_STORED)
@skipUnless(zlib, "requires zlib")
def test_open_deflated(self):
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
self.zip_open_test(f, zipfile.ZIP_DEFLATED)
def zip_random_open_test(self, f, compression): def zip_random_open_test(self, f, compression):
self.make_test_archive(f, compression) self.make_test_archive(f, compression)
@ -1017,6 +1092,11 @@ class TestsWithRandomBinaryFiles(unittest.TestCase):
for f in (TESTFN2, TemporaryFile(), StringIO()): for f in (TESTFN2, TemporaryFile(), StringIO()):
self.zip_random_open_test(f, zipfile.ZIP_STORED) self.zip_random_open_test(f, zipfile.ZIP_STORED)
@skipUnless(zlib, "requires zlib")
def test_random_open_deflated(self):
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
self.zip_random_open_test(f, zipfile.ZIP_DEFLATED)
@skipUnless(zlib, "requires zlib") @skipUnless(zlib, "requires zlib")
class TestsWithMultipleOpens(unittest.TestCase): class TestsWithMultipleOpens(unittest.TestCase):

View File

@ -493,6 +493,12 @@ class ZipExtFile(io.BufferedIOBase):
self.mode = mode self.mode = mode
self.name = zipinfo.filename self.name = zipinfo.filename
if hasattr(zipinfo, 'CRC'):
self._expected_crc = zipinfo.CRC
self._running_crc = crc32(b'') & 0xffffffff
else:
self._expected_crc = None
def readline(self, limit=-1): def readline(self, limit=-1):
"""Read and return a line from the stream. """Read and return a line from the stream.
@ -570,6 +576,16 @@ class ZipExtFile(io.BufferedIOBase):
return buf return buf
def _update_crc(self, newdata, eof):
# Update the CRC using the given data.
if self._expected_crc is None:
# No need to compute the CRC if we don't have a reference value
return
self._running_crc = crc32(newdata, self._running_crc) & 0xffffffff
# Check the CRC if we're at the end of the file
if eof and self._running_crc != self._expected_crc:
raise BadZipfile("Bad CRC-32 for file %r" % self.name)
def read1(self, n): def read1(self, n):
"""Read up to n bytes with at most one read() system call.""" """Read up to n bytes with at most one read() system call."""
@ -593,6 +609,7 @@ class ZipExtFile(io.BufferedIOBase):
data = ''.join(map(self._decrypter, data)) data = ''.join(map(self._decrypter, data))
if self._compress_type == ZIP_STORED: if self._compress_type == ZIP_STORED:
self._update_crc(data, eof=(self._compress_left==0))
self._readbuffer = self._readbuffer[self._offset:] + data self._readbuffer = self._readbuffer[self._offset:] + data
self._offset = 0 self._offset = 0
else: else:
@ -608,9 +625,11 @@ class ZipExtFile(io.BufferedIOBase):
) )
self._unconsumed = self._decompressor.unconsumed_tail self._unconsumed = self._decompressor.unconsumed_tail
if len(self._unconsumed) == 0 and self._compress_left == 0: eof = len(self._unconsumed) == 0 and self._compress_left == 0
if eof:
data += self._decompressor.flush() data += self._decompressor.flush()
self._update_crc(data, eof=eof)
self._readbuffer = self._readbuffer[self._offset:] + data self._readbuffer = self._readbuffer[self._offset:] + data
self._offset = 0 self._offset = 0
@ -1349,7 +1368,9 @@ def main(args = None):
print USAGE print USAGE
sys.exit(1) sys.exit(1)
zf = ZipFile(args[1], 'r') zf = ZipFile(args[1], 'r')
zf.testzip() badfile = zf.testzip()
if badfile:
print("The following enclosed file is corrupted: {!r}".format(badfile))
print "Done testing" print "Done testing"
elif args[0] == '-e': elif args[0] == '-e':

View File

@ -29,6 +29,10 @@ Core and Builtins
Library Library
------- -------
- Issue #7467: when reading a file from a ZIP archive, its CRC is checked
and a BadZipfile error is raised if it doesn't match (as used to be the
case in Python 2.5 and earlier).
- Issue #9550: a BufferedReader could issue an additional read when the - Issue #9550: a BufferedReader could issue an additional read when the
original read request had been satisfied, which could block indefinitely original read request had been satisfied, which could block indefinitely
when the underlying raw IO channel was e.g. a socket. Report and original when the underlying raw IO channel was e.g. a socket. Report and original