]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-67837, gh-112998: Fix dirs creation in concurrent extraction (GH-115082)
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 11 Feb 2024 10:38:07 +0000 (12:38 +0200)
committerGitHub <noreply@github.com>
Sun, 11 Feb 2024 10:38:07 +0000 (12:38 +0200)
Avoid race conditions in the creation of directories during concurrent
extraction in tarfile and zipfile.

Co-authored-by: Samantha Hughes <shughes-uk@users.noreply.github.com>
Co-authored-by: Peder Bergebakken Sundt <pbsds@hotmail.com>
Lib/tarfile.py
Lib/test/archiver_tests.py
Lib/zipfile/__init__.py
Misc/NEWS.d/next/Library/2024-02-06-15-16-28.gh-issue-67837._JKa73.rst [new file with mode: 0644]

index 9775040cbe372cd3fbfd1e49d9895293240a54d0..f4dd0fdab4a3e4dbc1d943e35b0065786f33188d 100755 (executable)
@@ -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))
index 1a4bbb9e5706c53754fd6956ef35b91cf66f8ad3..24745941b089239fb8584bcd729cb8fe91963b8d 100644 (file)
@@ -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')))
index 8005b4b34ccf76c2e6b0ca46796af6f4bc744bcf..cc08f602fe44e0505297e4b182a50da191fc54d0 100644 (file)
@@ -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 (file)
index 0000000..340b65f
--- /dev/null
@@ -0,0 +1,2 @@
+Avoid race conditions in the creation of directories during concurrent
+extraction in :mod:`tarfile` and :mod:`zipfile`.