diff --git a/Lib/zipfile.py b/Lib/zipfile.py index e1d07f2a5237bb..fcfced35200b8e 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -1677,14 +1677,24 @@ def _extract_member(self, member, targetpath, pwd): if upperdirs and not os.path.exists(upperdirs): os.makedirs(upperdirs) + # Extract the member if member.is_dir(): if not os.path.isdir(targetpath): os.mkdir(targetpath) - return targetpath - - with self.open(member, pwd=pwd) as source, \ - open(targetpath, "wb") as target: - shutil.copyfileobj(source, target) + else: + with self.open(member, pwd=pwd) as source, \ + open(targetpath, "wb") as target: + shutil.copyfileobj(source, target) + + # Preserve the permissions if we're on a posix system and the create_system was UNIX + # In the zip specification create_system 3 is UNIX + # See https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT CHAPTER 4.4.2.2 + zip_create_system_unix = 3 + if member.create_system == zip_create_system_unix and os.name == "posix": + # The first 16 bits contain the UNIX permission attributes + # See https://stackoverflow.com/a/46837272 + attr = member.external_attr >> 16 + os.chmod(targetpath, attr) return targetpath diff --git a/Misc/NEWS.d/next/Library/2020-03-03-13-50-37.bpo-15795.qQkxN0.rst b/Misc/NEWS.d/next/Library/2020-03-03-13-50-37.bpo-15795.qQkxN0.rst new file mode 100644 index 00000000000000..6a8f1ae5aea783 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-03-03-13-50-37.bpo-15795.qQkxN0.rst @@ -0,0 +1 @@ +Preserve permissions when extracting zip files on POSIX systems. Only do so if the zip file was created on a Unix-like system. \ No newline at end of file