Commit e9302b55 authored by Adrián Pérez de Castro's avatar Adrián Pérez de Castro Committed by Nirbheek Chauhan
Browse files

hacks: support symbolic links in .zip files

Add support for re-creating symbolic links when unpacking .zip files,
copying the complete implementation of ZipFile._extract_member() and
adding the needed modifications. As a safeguard to avoid following
symlinks when re-unpacking the same file, which could result in wrong
output, always unlink each destination file before creating it.

This is needed for newer versions of the Android NDK which e.g. link
"llvm-ranlib" to "llvm-ar" and a few others.

Originally reported at

Part-of: <!877>
parent ee6af76a
Pipeline #599329 passed with stages
in 121 minutes and 17 seconds
......@@ -139,15 +139,63 @@ from zipfile import ZipFile as zipfile_ZipFile
class ZipFile(zipfile_ZipFile):
def _extract_member(self, member, targetpath, pwd):
"""Extract the ZipInfo object 'member' to a physical
file on the path targetpath.
if not isinstance(member, zipfile.ZipInfo):
member = self.getinfo(member)
path = super()._extract_member(member, targetpath, pwd)
# build the destination pathname, replacing
# forward slashes to platform specific separators.
arcname = member.filename.replace('/', os.path.sep)
if os.path.altsep:
arcname = arcname.replace(os.path.altsep, os.path.sep)
# interpret absolute pathname as relative, remove drive letter or
# UNC path, redundant separators, "." and ".." components.
arcname = os.path.splitdrive(arcname)[1]
invalid_path_parts = ('', os.path.curdir, os.path.pardir)
arcname = os.path.sep.join(x for x in arcname.split(os.path.sep)
if x not in invalid_path_parts)
if os.path.sep == '\\':
# filter illegal characters on Windows
arcname = self._sanitize_windows_name(arcname, os.path.sep)
targetpath = os.path.join(targetpath, arcname)
targetpath = os.path.normpath(targetpath)
# Create all upper directories if necessary.
upperdirs = os.path.dirname(targetpath)
if upperdirs and not os.path.exists(upperdirs):
# Unlink before extracting, this ensures that if there is a symbolic
# link in place it will not be followed to the (possibly non existing)
# destination.
except FileNotFoundError:
if member.is_dir():
if not os.path.isdir(targetpath):
return targetpath
# Handle symlinks.
if (member.external_attr >> 28) == 0xA:
os.symlink(, targetpath)
return targetpath
with, pwd=pwd) as source, \
open(targetpath, "wb") as target:
shutil.copyfileobj(source, target)
attr = member.external_attr >> 16
if attr != 0:
os.chmod(path, attr)
return path
os.chmod(targetpath, attr)
return targetpath
zipfile.ZipFile = ZipFile
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment