Rewrite unzip() to be resilient to long Windows path names.

This commit is contained in:
Jukka Jylänki
2017-03-23 01:37:03 +02:00
parent 836d2ce941
commit 6320c01407

40
emsdk
View File

@@ -309,6 +309,16 @@ def untargz(source_filename, dest_dir, unpack_even_if_exists=False):
#tfile.extractall(dest_dir) #tfile.extractall(dest_dir)
return True return True
# On Windows, it is not possible to reference path names that are longer than ~260 characters, unless the path is referenced via a "\\?\" prefix.
# See https://msdn.microsoft.com/en-us/library/aa365247.aspx#maxpath and http://stackoverflow.com/questions/3555527/python-win32-filename-length-workaround
# In that mode, forward slashes cannot be used as delimiters.
def fix_potentially_long_windows_pathname(pathname):
if not WINDOWS: return pathname
# Test if emsdk calls fix_potentially_long_windows_pathname() with long relative paths (which is problematic)
if not os.path.isabs(pathname) and len(pathname) > 200: print('Warning: Seeing a relative path "' + pathname + '" which is dangerously long for being referenced as a short Windows path name. Refactor emsdk to be able to handle this!')
if pathname.startswith('\\\\?\\'): return pathname
return '\\\\?\\' + os.path.normpath(pathname.replace('/', '\\'))
# http://stackoverflow.com/questions/12886768/simple-way-to-unzip-file-in-python-on-all-oses # http://stackoverflow.com/questions/12886768/simple-way-to-unzip-file-in-python-on-all-oses
def unzip(source_filename, dest_dir, unpack_even_if_exists=False): def unzip(source_filename, dest_dir, unpack_even_if_exists=False):
if VERBOSE: print('unzip(source_filename=' + source_filename + ', dest_dir=' + dest_dir + ')') if VERBOSE: print('unzip(source_filename=' + source_filename + ', dest_dir=' + dest_dir + ')')
@@ -340,24 +350,22 @@ def unzip(source_filename, dest_dir, unpack_even_if_exists=False):
# Now do the actual decompress. # Now do the actual decompress.
for member in zf.infolist(): for member in zf.infolist():
# Path traversal defense copied from zf.extract(member, fix_potentially_long_windows_pathname(unzip_to_dir))
# http://hg.python.org/cpython/file/tip/Lib/http/server.py#l789
words = member.filename.split('/') # Move the extracted file to its final location without the base directory name, if we are stripping that away.
path = unzip_to_dir if common_subdir:
for word in words[:-1]: if not member.filename.startswith(common_subdir):
drive, word = os.path.splitdrive(word) raise Exception('Unexpected filename "' + member.filename + '"!')
head, word = os.path.split(word) stripped_filename = '.' + member.filename[len(common_subdir):]
if word in (os.curdir, os.pardir, ''): continue final_dst_filename = os.path.join(dest_dir, stripped_filename)
path = os.path.join(path, word) if stripped_filename.endswith('/'): # Directory?
zf.extract(member, unzip_to_dir) d = fix_potentially_long_windows_pathname(final_dst_filename)
if not os.path.isdir(d): os.mkdir(d)
else:
tmp_dst_filename = os.path.join(unzip_to_dir, member.filename)
os.rename(fix_potentially_long_windows_pathname(tmp_dst_filename), fix_potentially_long_windows_pathname(final_dst_filename))
if common_subdir: if common_subdir:
try:
if os.path.exists(dest_dir):
remove_tree(dest_dir)
except:
pass
shutil.copytree(os.path.join(unzip_to_dir, common_subdir), dest_dir)
try: try:
remove_tree(unzip_to_dir) remove_tree(unzip_to_dir)
except: except: