Browse Source

fix: strip extra data from release zips (#22891)

Andrea Brancaleoni 5 years ago
parent
commit
884b46fce7
1 changed files with 78 additions and 9 deletions
  1. 78 9
      script/release/uploaders/upload.py

+ 78 - 9
script/release/uploaders/upload.py

@@ -182,35 +182,104 @@ def zero_zip_date_time(fname):
 
 
 def _zero_zip_date_time(zip_):
-  """ Code under MIT from https://github.com/Code0x58/python-stripzip/blob/f1980fcfc55cb6ee1f83a2f72244dd38b3b649f4/stripzip.py """
-  archive_size = os.fstat(zip_.fileno()).st_size
+  def purify_extra_data(mm, offset, length):
+    extra_header_struct = Struct("<HH")
+    # 0. id
+    # 1. length
+    STRIPZIP_OPTION_HEADER = 0xFFFF
+    EXTENDED_TIME_DATA = 0x5455
+    # Some sort of extended time data, see
+    # ftp://ftp.info-zip.org/pub/infozip/src/zip30.zip ./proginfo/extrafld.txt
+    # fallthrough
+    UNIX_EXTRA_DATA = 0x7875
+    # Unix extra data; UID / GID stuff, see
+    # ftp://ftp.info-zip.org/pub/infozip/src/zip30.zip ./proginfo/extrafld.txt
+    mlen = offset + length
+
+    while offset < mlen:
+      values = list(extra_header_struct.unpack_from(mm, offset))
+      _, header_length = values
+      extra_struct = Struct("<HH" + "B" * header_length)
+      values = list(extra_struct.unpack_from(mm, offset))
+      header_id, header_length, rest = values[0], values[1], values[2:]
+
+      if header_id in (EXTENDED_TIME_DATA, UNIX_EXTRA_DATA):
+        values[0] = STRIPZIP_OPTION_HEADER
+        for i in xrange(2, len(values)):
+          values[i] = 0xff
+        extra_struct.pack_into(mm, offset, *values)
+      elif header_id != STRIPZIP_OPTION_HEADER:
+        return False
+
+      offset += extra_header_struct.size + header_length
+
+    return True
+
+  FILE_HEADER_SIGNATURE = 0x04034b50
+  CENDIR_HEADER_SIGNATURE = 0x02014b50
 
+  archive_size = os.fstat(zip_.fileno()).st_size
   signature_struct = Struct("<L")
   local_file_header_struct = Struct("<LHHHHHLLLHH")
+  # 0. L signature
+  # 1. H version_needed
+  # 2. H gp_bits
+  # 3. H compression_method
+  # 4. H last_mod_time
+  # 5. H last_mod_date
+  # 6. L crc32
+  # 7. L compressed_size
+  # 8. L uncompressed_size
+  # 9. H name_length
+  # 10. H extra_field_length
   central_directory_header_struct = Struct("<LHHHHHHLLLHHHHHLL")
-
+  # 0. L signature
+  # 1. H version_made_by
+  # 2. H version_needed
+  # 3. H gp_bits
+  # 4. H compression_method
+  # 5. H last_mod_time
+  # 6. H last_mod_date
+  # 7. L crc32
+  # 8. L compressed_size
+  # 9. L uncompressed_size
+  # 10. H file_name_length
+  # 11. H extra_field_length
+  # 12. H file_comment_length
+  # 13. H disk_number_start
+  # 14. H internal_attr
+  # 15. L external_attr
+  # 16. L rel_offset_local_header
   offset = 0
 
   mm = mmap.mmap(zip_.fileno(), 0)
   while offset < archive_size:
-    if signature_struct.unpack_from(mm, offset) != (0x04034b50,):
+    if signature_struct.unpack_from(mm, offset) != (FILE_HEADER_SIGNATURE,):
       break
     values = list(local_file_header_struct.unpack_from(mm, offset))
-    _, _, _, _, _, _, _, a, _, b, c = values
+    _, _, _, _, _, _, _, compressed_size, _, name_length, extra_field_length = values
+    # reset last_mod_time
     values[4] = 0
+    # reset last_mod_date
     values[5] = 0x21
     local_file_header_struct.pack_into(mm, offset, *values)
-    offset += local_file_header_struct.size + a + b + c
+    offset += local_file_header_struct.size + compressed_size + name_length + extra_field_length
+    if extra_field_length != 0:
+      purify_extra_data(mm, offset - extra_field_length - compressed_size, extra_field_length)
 
   while offset < archive_size:
-    if signature_struct.unpack_from(mm, offset) != (0x02014b50,):
+    if signature_struct.unpack_from(mm, offset) != (CENDIR_HEADER_SIGNATURE,):
       break
     values = list(central_directory_header_struct.unpack_from(mm, offset))
-    _, _, _, _, _, _, _, _, _, _, a, b, c, _, _, _, _ = values
+    _, _, _, _, _, _, _, _, _, _, file_name_length, extra_field_length, file_comment_length, _, _, _, _ = values
+    # reset last_mod_time
     values[5] = 0
+    # reset last_mod_date
     values[6] = 0x21
     central_directory_header_struct.pack_into(mm, offset, *values)
-    offset += central_directory_header_struct.size + a + b + c
+    offset += central_directory_header_struct.size + file_name_length + extra_field_length + file_comment_length
+    if extra_field_length != 0:
+      purify_extra_data(mm, offset - extra_field_length, extra_field_length)
 
   if offset == 0:
     raise NonZipFileError(zip_.name)