summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xLib/tarfile.py2
-rw-r--r--Lib/test/archiver_tests.py22
-rw-r--r--Lib/zipfile/__init__.py8
-rw-r--r--Misc/NEWS.d/next/Library/2024-02-06-15-16-28.gh-issue-67837._JKa73.rst2
4 files changed, 31 insertions, 3 deletions
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 9775040..f4dd0fd 100755
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -2411,7 +2411,7 @@ class TarFile(object):
if upperdirs and not os.path.exists(upperdirs):
# Create directories that are not part of the archive with
# default permissions.
- os.makedirs(upperdirs)
+ os.makedirs(upperdirs, exist_ok=True)
if tarinfo.islnk() or tarinfo.issym():
self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname))
diff --git a/Lib/test/archiver_tests.py b/Lib/test/archiver_tests.py
index 1a4bbb9..2474594 100644
--- a/Lib/test/archiver_tests.py
+++ b/Lib/test/archiver_tests.py
@@ -3,6 +3,7 @@
import os
import sys
+from test.support import swap_attr
from test.support import os_helper
class OverwriteTests:
@@ -153,3 +154,24 @@ class OverwriteTests:
self.extractall(ar)
self.assertTrue(os.path.islink(target))
self.assertFalse(os.path.exists(target2))
+
+ def test_concurrent_extract_dir(self):
+ target = os.path.join(self.testdir, 'test')
+ def concurrent_mkdir(*args, **kwargs):
+ orig_mkdir(*args, **kwargs)
+ orig_mkdir(*args, **kwargs)
+ with swap_attr(os, 'mkdir', concurrent_mkdir) as orig_mkdir:
+ with self.open(self.ar_with_dir) as ar:
+ self.extractall(ar)
+ self.assertTrue(os.path.isdir(target))
+
+ def test_concurrent_extract_implicit_dir(self):
+ target = os.path.join(self.testdir, 'test')
+ def concurrent_mkdir(*args, **kwargs):
+ orig_mkdir(*args, **kwargs)
+ orig_mkdir(*args, **kwargs)
+ with swap_attr(os, 'mkdir', concurrent_mkdir) as orig_mkdir:
+ with self.open(self.ar_with_implicit_dir) as ar:
+ self.extractall(ar)
+ self.assertTrue(os.path.isdir(target))
+ self.assertTrue(os.path.isfile(os.path.join(target, 'file')))
diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py
index 8005b4b..cc08f60 100644
--- a/Lib/zipfile/__init__.py
+++ b/Lib/zipfile/__init__.py
@@ -1802,11 +1802,15 @@ class ZipFile:
# Create all upper directories if necessary.
upperdirs = os.path.dirname(targetpath)
if upperdirs and not os.path.exists(upperdirs):
- os.makedirs(upperdirs)
+ os.makedirs(upperdirs, exist_ok=True)
if member.is_dir():
if not os.path.isdir(targetpath):
- os.mkdir(targetpath)
+ try:
+ os.mkdir(targetpath)
+ except FileExistsError:
+ if not os.path.isdir(targetpath):
+ raise
return targetpath
with self.open(member, pwd=pwd) as source, \
diff --git a/Misc/NEWS.d/next/Library/2024-02-06-15-16-28.gh-issue-67837._JKa73.rst b/Misc/NEWS.d/next/Library/2024-02-06-15-16-28.gh-issue-67837._JKa73.rst
new file mode 100644
index 0000000..340b65f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-02-06-15-16-28.gh-issue-67837._JKa73.rst
@@ -0,0 +1,2 @@
+Avoid race conditions in the creation of directories during concurrent
+extraction in :mod:`tarfile` and :mod:`zipfile`.