Commit 02512fb1 authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #16408: Fix file descriptors not being closed in error conditions in the zipfile module.

Patch by Serhiy Storchaka.
parent 115bc79d
...@@ -718,8 +718,9 @@ class ZipFile(object): ...@@ -718,8 +718,9 @@ class ZipFile(object):
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
...@@ -738,10 +739,13 @@ class ZipFile(object): ...@@ -738,10 +739,13 @@ class ZipFile(object):
# 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: raise RuntimeError('Mode must be "r", "w" or "a"')
self.fp.close() except:
fp = self.fp
self.fp = None self.fp = None
raise RuntimeError, 'Mode must be "r", "w" or "a"' if not self._filePassed:
fp.close()
raise
def __enter__(self): def __enter__(self):
return self return self
...@@ -749,17 +753,6 @@ class ZipFile(object): ...@@ -749,17 +753,6 @@ class ZipFile(object):
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
...@@ -853,7 +846,7 @@ class ZipFile(object): ...@@ -853,7 +846,7 @@ class ZipFile(object):
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:
...@@ -909,6 +902,7 @@ class ZipFile(object): ...@@ -909,6 +902,7 @@ class ZipFile(object):
zef_file = open(self.filename, 'rb') zef_file = open(self.filename, 'rb')
should_close = True should_close = True
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
...@@ -963,6 +957,10 @@ class ZipFile(object): ...@@ -963,6 +957,10 @@ class ZipFile(object):
return ZipExtFile(zef_file, mode, zinfo, zd, return ZipExtFile(zef_file, mode, zinfo, zd,
close_fileobj=should_close) close_fileobj=should_close)
except:
if should_close:
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,
...@@ -1019,11 +1017,9 @@ class ZipFile(object): ...@@ -1019,11 +1017,9 @@ class ZipFile(object):
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 = file(targetpath, "wb") file(targetpath, "wb") as target:
shutil.copyfileobj(source, target) shutil.copyfileobj(source, target)
source.close()
target.close()
return targetpath return targetpath
...@@ -1184,6 +1180,7 @@ class ZipFile(object): ...@@ -1184,6 +1180,7 @@ class ZipFile(object):
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()
...@@ -1276,10 +1273,11 @@ class ZipFile(object): ...@@ -1276,10 +1273,11 @@ class ZipFile(object):
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):
...@@ -1401,15 +1399,14 @@ def main(args = None): ...@@ -1401,15 +1399,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))
...@@ -1420,7 +1417,7 @@ def main(args = None): ...@@ -1420,7 +1417,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('./'):
...@@ -1433,7 +1430,6 @@ def main(args = None): ...@@ -1433,7 +1430,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:
...@@ -1449,11 +1445,9 @@ def main(args = None): ...@@ -1449,11 +1445,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()
...@@ -148,6 +148,9 @@ Core and Builtins ...@@ -148,6 +148,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 #16327: The subprocess module no longer leaks file descriptors - Issue #16327: The subprocess module no longer leaks file descriptors
used for stdin/stdout/stderr pipes to the child when fork() fails. used for stdin/stdout/stderr pipes to the child when fork() fails.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment