]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.11] gh-117084: Fix ZIP file extraction for directory entry names with backslashes...
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 22 Mar 2024 19:24:27 +0000 (21:24 +0200)
committerGitHub <noreply@github.com>
Fri, 22 Mar 2024 19:24:27 +0000 (19:24 +0000)
(cherry picked from commit f3fee231d359979133e1d58085f43277c41476d0)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
(cherry picked from commit 567ab3bd15398c8c7b791f3e376ae3e3c0bbe079)

Lib/test/test_zipfile.py
Lib/test/zipdir_backslash.zip [new file with mode: 0644]
Lib/zipfile.py
Misc/NEWS.d/next/Library/2024-03-21-17-07-38.gh-issue-117084.w1mTpT.rst [new file with mode: 0644]

index bb5dad2d17bbd44ab9efacaa0571a863c8313d56..4de6f379a4789157ee7bc9ca1cd399b2162815d6 100644 (file)
@@ -2882,6 +2882,22 @@ class TestWithDirectory(unittest.TestCase):
         os.mkdir(os.path.join(TESTFN2, "a"))
         self.test_extract_dir()
 
+    def test_extract_dir_backslash(self):
+        zfname = findfile("zipdir_backslash.zip")
+        with zipfile.ZipFile(zfname) as zipf:
+            zipf.extractall(TESTFN2)
+        if os.name == 'nt':
+            self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a")))
+            self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b")))
+            self.assertTrue(os.path.isfile(os.path.join(TESTFN2, "a", "b", "c")))
+            self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "d")))
+            self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "d", "e")))
+        else:
+            self.assertTrue(os.path.isfile(os.path.join(TESTFN2, "a\\b\\c")))
+            self.assertTrue(os.path.isfile(os.path.join(TESTFN2, "d\\e\\")))
+            self.assertFalse(os.path.exists(os.path.join(TESTFN2, "a")))
+            self.assertFalse(os.path.exists(os.path.join(TESTFN2, "d")))
+
     def test_write_dir(self):
         dirpath = os.path.join(TESTFN2, "x")
         os.mkdir(dirpath)
diff --git a/Lib/test/zipdir_backslash.zip b/Lib/test/zipdir_backslash.zip
new file mode 100644 (file)
index 0000000..979126e
Binary files /dev/null and b/Lib/test/zipdir_backslash.zip differ
index 058d7163ea17ac281cfc8dbc46bec448b07895ce..86829abce4edaa41a168d0b2e841c0964f35e2ae 100644 (file)
@@ -559,7 +559,15 @@ class ZipInfo (object):
 
     def is_dir(self):
         """Return True if this archive member is a directory."""
-        return self.filename[-1] == '/'
+        if self.filename.endswith('/'):
+            return True
+        # The ZIP format specification requires to use forward slashes
+        # as the directory separator, but in practice some ZIP files
+        # created on Windows can use backward slashes.  For compatibility
+        # with the extraction code which already handles this:
+        if os.path.altsep:
+            return self.filename.endswith((os.path.sep, os.path.altsep))
+        return False
 
 
 # ZIP encryption uses the CRC32 one-byte primitive for scrambling some
diff --git a/Misc/NEWS.d/next/Library/2024-03-21-17-07-38.gh-issue-117084.w1mTpT.rst b/Misc/NEWS.d/next/Library/2024-03-21-17-07-38.gh-issue-117084.w1mTpT.rst
new file mode 100644 (file)
index 0000000..6e7790e
--- /dev/null
@@ -0,0 +1,2 @@
+Fix :mod:`zipfile` extraction for directory entries with the name containing
+backslashes on Windows.