Issue #16408: Fix file descriptors not being closed in error conditions in the zipfile module.
Patch by Serhiy Storchaka.
This commit is contained in:
parent
a39a22dc0b
commit
17babc5e97
|
@ -719,8 +719,9 @@ class ZipFile:
|
||||||
self.fp = file
|
self.fp = file
|
||||||
self.filename = getattr(file, 'name', None)
|
self.filename = getattr(file, 'name', None)
|
||||||
|
|
||||||
|
try:
|
||||||
if key == 'r':
|
if key == 'r':
|
||||||
self._GetContents()
|
self._RealGetContents()
|
||||||
elif key == 'w':
|
elif key == 'w':
|
||||||
# set the modified flag so central directory gets written
|
# set the modified flag so central directory gets written
|
||||||
# even if no files are added to the archive
|
# even if no files are added to the archive
|
||||||
|
@ -739,10 +740,13 @@ class ZipFile:
|
||||||
# even if no files are added to the archive
|
# even if no files are added to the archive
|
||||||
self._didModify = True
|
self._didModify = True
|
||||||
else:
|
else:
|
||||||
if not self._filePassed:
|
|
||||||
self.fp.close()
|
|
||||||
self.fp = None
|
|
||||||
raise RuntimeError('Mode must be "r", "w" or "a"')
|
raise RuntimeError('Mode must be "r", "w" or "a"')
|
||||||
|
except:
|
||||||
|
fp = self.fp
|
||||||
|
self.fp = None
|
||||||
|
if not self._filePassed:
|
||||||
|
fp.close()
|
||||||
|
raise
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
@ -750,17 +754,6 @@ class ZipFile:
|
||||||
def __exit__(self, type, value, traceback):
|
def __exit__(self, type, value, traceback):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def _GetContents(self):
|
|
||||||
"""Read the directory, making sure we close the file if the format
|
|
||||||
is bad."""
|
|
||||||
try:
|
|
||||||
self._RealGetContents()
|
|
||||||
except BadZipFile:
|
|
||||||
if not self._filePassed:
|
|
||||||
self.fp.close()
|
|
||||||
self.fp = None
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _RealGetContents(self):
|
def _RealGetContents(self):
|
||||||
"""Read in the table of contents for the ZIP file."""
|
"""Read in the table of contents for the ZIP file."""
|
||||||
fp = self.fp
|
fp = self.fp
|
||||||
|
@ -862,7 +855,7 @@ class ZipFile:
|
||||||
try:
|
try:
|
||||||
# Read by chunks, to avoid an OverflowError or a
|
# Read by chunks, to avoid an OverflowError or a
|
||||||
# MemoryError with very large embedded files.
|
# MemoryError with very large embedded files.
|
||||||
f = self.open(zinfo.filename, "r")
|
with self.open(zinfo.filename, "r") as f:
|
||||||
while f.read(chunk_size): # Check CRC-32
|
while f.read(chunk_size): # Check CRC-32
|
||||||
pass
|
pass
|
||||||
except BadZipFile:
|
except BadZipFile:
|
||||||
|
@ -926,18 +919,14 @@ class ZipFile:
|
||||||
else:
|
else:
|
||||||
zef_file = io.open(self.filename, 'rb')
|
zef_file = io.open(self.filename, 'rb')
|
||||||
|
|
||||||
|
try:
|
||||||
# Make sure we have an info object
|
# Make sure we have an info object
|
||||||
if isinstance(name, ZipInfo):
|
if isinstance(name, ZipInfo):
|
||||||
# 'name' is already an info object
|
# 'name' is already an info object
|
||||||
zinfo = name
|
zinfo = name
|
||||||
else:
|
else:
|
||||||
# Get info object for name
|
# Get info object for name
|
||||||
try:
|
|
||||||
zinfo = self.getinfo(name)
|
zinfo = self.getinfo(name)
|
||||||
except KeyError:
|
|
||||||
if not self._filePassed:
|
|
||||||
zef_file.close()
|
|
||||||
raise
|
|
||||||
zef_file.seek(zinfo.header_offset, 0)
|
zef_file.seek(zinfo.header_offset, 0)
|
||||||
|
|
||||||
# Skip the file header:
|
# Skip the file header:
|
||||||
|
@ -957,8 +946,6 @@ class ZipFile:
|
||||||
fname_str = fname.decode("cp437")
|
fname_str = fname.decode("cp437")
|
||||||
|
|
||||||
if fname_str != zinfo.orig_filename:
|
if fname_str != zinfo.orig_filename:
|
||||||
if not self._filePassed:
|
|
||||||
zef_file.close()
|
|
||||||
raise BadZipFile(
|
raise BadZipFile(
|
||||||
'File name in directory %r and header %r differ.'
|
'File name in directory %r and header %r differ.'
|
||||||
% (zinfo.orig_filename, fname))
|
% (zinfo.orig_filename, fname))
|
||||||
|
@ -970,10 +957,8 @@ class ZipFile:
|
||||||
if not pwd:
|
if not pwd:
|
||||||
pwd = self.pwd
|
pwd = self.pwd
|
||||||
if not pwd:
|
if not pwd:
|
||||||
if not self._filePassed:
|
raise RuntimeError("File %s is encrypted, password "
|
||||||
zef_file.close()
|
"required for extraction" % name)
|
||||||
raise RuntimeError("File %s is encrypted, "
|
|
||||||
"password required for extraction" % name)
|
|
||||||
|
|
||||||
zd = _ZipDecrypter(pwd)
|
zd = _ZipDecrypter(pwd)
|
||||||
# The first 12 bytes in the cypher stream is an encryption header
|
# The first 12 bytes in the cypher stream is an encryption header
|
||||||
|
@ -990,12 +975,14 @@ class ZipFile:
|
||||||
# compare against the CRC otherwise
|
# compare against the CRC otherwise
|
||||||
check_byte = (zinfo.CRC >> 24) & 0xff
|
check_byte = (zinfo.CRC >> 24) & 0xff
|
||||||
if h[11] != check_byte:
|
if h[11] != check_byte:
|
||||||
if not self._filePassed:
|
|
||||||
zef_file.close()
|
|
||||||
raise RuntimeError("Bad password for file", name)
|
raise RuntimeError("Bad password for file", name)
|
||||||
|
|
||||||
return ZipExtFile(zef_file, mode, zinfo, zd,
|
return ZipExtFile(zef_file, mode, zinfo, zd,
|
||||||
close_fileobj=not self._filePassed)
|
close_fileobj=not self._filePassed)
|
||||||
|
except:
|
||||||
|
if not self._filePassed:
|
||||||
|
zef_file.close()
|
||||||
|
raise
|
||||||
|
|
||||||
def extract(self, member, path=None, pwd=None):
|
def extract(self, member, path=None, pwd=None):
|
||||||
"""Extract a member from the archive to the current working directory,
|
"""Extract a member from the archive to the current working directory,
|
||||||
|
@ -1052,11 +1039,9 @@ class ZipFile:
|
||||||
os.mkdir(targetpath)
|
os.mkdir(targetpath)
|
||||||
return targetpath
|
return targetpath
|
||||||
|
|
||||||
source = self.open(member, pwd=pwd)
|
with self.open(member, pwd=pwd) as source, \
|
||||||
target = open(targetpath, "wb")
|
open(targetpath, "wb") as target:
|
||||||
shutil.copyfileobj(source, target)
|
shutil.copyfileobj(source, target)
|
||||||
source.close()
|
|
||||||
target.close()
|
|
||||||
|
|
||||||
return targetpath
|
return targetpath
|
||||||
|
|
||||||
|
@ -1220,6 +1205,7 @@ class ZipFile:
|
||||||
if self.fp is None:
|
if self.fp is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
if self.mode in ("w", "a") and self._didModify: # write ending records
|
if self.mode in ("w", "a") and self._didModify: # write ending records
|
||||||
count = 0
|
count = 0
|
||||||
pos1 = self.fp.tell()
|
pos1 = self.fp.tell()
|
||||||
|
@ -1311,10 +1297,11 @@ class ZipFile:
|
||||||
self.fp.write(endrec)
|
self.fp.write(endrec)
|
||||||
self.fp.write(self._comment)
|
self.fp.write(self._comment)
|
||||||
self.fp.flush()
|
self.fp.flush()
|
||||||
|
finally:
|
||||||
if not self._filePassed:
|
fp = self.fp
|
||||||
self.fp.close()
|
|
||||||
self.fp = None
|
self.fp = None
|
||||||
|
if not self._filePassed:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
class PyZipFile(ZipFile):
|
class PyZipFile(ZipFile):
|
||||||
|
@ -1481,15 +1468,14 @@ def main(args = None):
|
||||||
if len(args) != 2:
|
if len(args) != 2:
|
||||||
print(USAGE)
|
print(USAGE)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
zf = ZipFile(args[1], 'r')
|
with ZipFile(args[1], 'r') as zf:
|
||||||
zf.printdir()
|
zf.printdir()
|
||||||
zf.close()
|
|
||||||
|
|
||||||
elif args[0] == '-t':
|
elif args[0] == '-t':
|
||||||
if len(args) != 2:
|
if len(args) != 2:
|
||||||
print(USAGE)
|
print(USAGE)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
zf = ZipFile(args[1], 'r')
|
with ZipFile(args[1], 'r') as zf:
|
||||||
badfile = zf.testzip()
|
badfile = zf.testzip()
|
||||||
if badfile:
|
if badfile:
|
||||||
print("The following enclosed file is corrupted: {!r}".format(badfile))
|
print("The following enclosed file is corrupted: {!r}".format(badfile))
|
||||||
|
@ -1500,7 +1486,7 @@ def main(args = None):
|
||||||
print(USAGE)
|
print(USAGE)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
zf = ZipFile(args[1], 'r')
|
with ZipFile(args[1], 'r') as zf:
|
||||||
out = args[2]
|
out = args[2]
|
||||||
for path in zf.namelist():
|
for path in zf.namelist():
|
||||||
if path.startswith('./'):
|
if path.startswith('./'):
|
||||||
|
@ -1513,7 +1499,6 @@ def main(args = None):
|
||||||
os.makedirs(tgtdir)
|
os.makedirs(tgtdir)
|
||||||
with open(tgt, 'wb') as fp:
|
with open(tgt, 'wb') as fp:
|
||||||
fp.write(zf.read(path))
|
fp.write(zf.read(path))
|
||||||
zf.close()
|
|
||||||
|
|
||||||
elif args[0] == '-c':
|
elif args[0] == '-c':
|
||||||
if len(args) < 3:
|
if len(args) < 3:
|
||||||
|
@ -1529,11 +1514,9 @@ def main(args = None):
|
||||||
os.path.join(path, nm), os.path.join(zippath, nm))
|
os.path.join(path, nm), os.path.join(zippath, nm))
|
||||||
# else: ignore
|
# else: ignore
|
||||||
|
|
||||||
zf = ZipFile(args[1], 'w', allowZip64=True)
|
with ZipFile(args[1], 'w', allowZip64=True) as zf:
|
||||||
for src in args[2:]:
|
for src in args[2:]:
|
||||||
addToZip(zf, src, os.path.basename(src))
|
addToZip(zf, src, os.path.basename(src))
|
||||||
|
|
||||||
zf.close()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -164,6 +164,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #16408: Fix file descriptors not being closed in error conditions
|
||||||
|
in the zipfile module. Patch by Serhiy Storchaka.
|
||||||
|
|
||||||
- Issue #16140: The subprocess module no longer double closes its child
|
- Issue #16140: The subprocess module no longer double closes its child
|
||||||
subprocess.PIPE parent file descriptors on child error prior to exec().
|
subprocess.PIPE parent file descriptors on child error prior to exec().
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue