Skip to content

Commit 0d82047

Browse files
[3.11] gh-117084: Fix ZIP file extraction for directory entry names with backslashes on Windows (GH-117129) (GH-117162) (GH-117165)
(cherry picked from commit f3fee23) Co-authored-by: Serhiy Storchaka <[email protected]> (cherry picked from commit 567ab3b)
1 parent da5efeb commit 0d82047

File tree

4 files changed

+27
-1
lines changed

4 files changed

+27
-1
lines changed

Lib/test/test_zipfile.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2882,6 +2882,22 @@ def test_bug_6050(self):
28822882
os.mkdir(os.path.join(TESTFN2, "a"))
28832883
self.test_extract_dir()
28842884

2885+
def test_extract_dir_backslash(self):
2886+
zfname = findfile("zipdir_backslash.zip")
2887+
with zipfile.ZipFile(zfname) as zipf:
2888+
zipf.extractall(TESTFN2)
2889+
if os.name == 'nt':
2890+
self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a")))
2891+
self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b")))
2892+
self.assertTrue(os.path.isfile(os.path.join(TESTFN2, "a", "b", "c")))
2893+
self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "d")))
2894+
self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "d", "e")))
2895+
else:
2896+
self.assertTrue(os.path.isfile(os.path.join(TESTFN2, "a\\b\\c")))
2897+
self.assertTrue(os.path.isfile(os.path.join(TESTFN2, "d\\e\\")))
2898+
self.assertFalse(os.path.exists(os.path.join(TESTFN2, "a")))
2899+
self.assertFalse(os.path.exists(os.path.join(TESTFN2, "d")))
2900+
28852901
def test_write_dir(self):
28862902
dirpath = os.path.join(TESTFN2, "x")
28872903
os.mkdir(dirpath)

Lib/test/zipdir_backslash.zip

192 Bytes
Binary file not shown.

Lib/zipfile.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,15 @@ def from_file(cls, filename, arcname=None, *, strict_timestamps=True):
559559

560560
def is_dir(self):
561561
"""Return True if this archive member is a directory."""
562-
return self.filename[-1] == '/'
562+
if self.filename.endswith('/'):
563+
return True
564+
# The ZIP format specification requires to use forward slashes
565+
# as the directory separator, but in practice some ZIP files
566+
# created on Windows can use backward slashes. For compatibility
567+
# with the extraction code which already handles this:
568+
if os.path.altsep:
569+
return self.filename.endswith((os.path.sep, os.path.altsep))
570+
return False
563571

564572

565573
# ZIP encryption uses the CRC32 one-byte primitive for scrambling some
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :mod:`zipfile` extraction for directory entries with the name containing
2+
backslashes on Windows.

0 commit comments

Comments
 (0)