Давным-давно, когда создавался формат ZIP и всем хватало 640KB памяти, никто не задумывался, что файл может быть >4GB и содержать в себе больше 65535 элементов, поэтому поддержка таких объемов прикручена костылями и называется ZIP64
. До сих пор этот формат плохо понимают Far Manager, 7-Zip и проводник седьмой винды (а говорят, что в Microsoft берут только гениальных программистов, возьмите меня, я умнее, чем выгляжу).
Расширение ZIP64
добавляет две основные фичи:
- структура
ZIP64 End Of Central Directory Record (EOCD64)
с 64-битными полями иZIP64 End Of Central Directory Locator
для поиска структурыEOCD64
- если значения некоторых полей
Central Directory Header
иLocal File Header
превышают максимальный, то эти значения записываются в Extra Field этих структур в специальном форматеZIP64 Extended Extra Field
А теперь подробнее про эти нововведения.
ZIP64 End Of Central Directory Locator
Используется для поиска структуры EOCD64
и находится сразу перед стандартным End Of Central Directory Record
.
struct EOCD64Locator { // Обязательная сигнатура, равна 0x07064b50 uint32_t signature; // Номер диска для поиска EOCD64 uint32_t diskNumber; // Смещение от начала файла до EOCD64 uint64_t eocd64Offset; // Количество дисков uint32_t totalDiskCount; }; |
End of central directory record
. Если 16-битные поля равны 0xFFFF
или 32-битные — 0xFFFFFFFF
, значит такая запись должна быть.
ZIP64 End Of Central Directory Record
Эта структура представляет собой 64-битную версию стандартного End Of Central Directory Record
. Находится по адресу EOCD64Locator.eocd64Offset
.
struct EOCD64 { // Обязательная сигнатура, равна 0x06064b50 uint32_t signature; // Размер записи EOCD64 uint64_t eocd64Size; // Версия для создания uint16_t versionMadeBy; // Версия для распаковки uint16_t versionToExtract; // Номер текущего диска uint32_t diskNumber; // Номер диска для поиска Central Directory uint32_t startDiskNumber; // Количество записей в Central Directory uint64_t numberCentralDirectoryRecord; // Всего записей в Central Directory uint64_t totalCentralDirectoryRecord; // Размер Central Directory uint64_t sizeOfCentralDirectory; // Смещение Central Directory uint64_t centralDirectoryOffset; // zip64 extensible data sector (переменной длины) uint8_t *dataSector; }; |
Extra Field
Если вы внимательно читали мои предыдущие заметки на тему ZIP формата (описание формата ZIP, чтение ZIP файла, запись ZIP файла), то могли заметить, что у записей типа Local File Header
и Central directory file header
есть поля под названием extraField
.
В это поле записываются расширенные данные, о которых не было известно на момент создания первой версии формата ZIP. В этом поле могут содержаться разные типы данных и реализуется это последовательностью структур типа ExtraFieldRecord
:
struct ExtraFieldRecord { // Заголовок (для ZIP64 extended information равен 0x0001) uint16_t headerId; // Размер данных uint16_t dataSize; // Какие-то данные размером dataSize uint8_t *data; }; |
ExtraFieldRecord.data
для ZIP64 extended information выглядят так:
struct ZIP64ExtendInformation { // Размер несжатых данных uint64_t uncompressedSize; // Размер сжатых данных uint64_t compressedSize; // Смещение Local File Header от начала файла uint64_t localFileHeaderOffset; // Номер диска для поиска uint32_t diskNumber; }; |
Например, структура Central directory file header
будет содержать 0xFFFFFFFF
только в поле localFileHeaderOffset
, значит в Extra Field будет лежать только значение localFileHeaderOffset
, а остальные поля будут отсутствовать.
Порядок действий
- определить наличие
EOCD64
проверкой полейEOCD
на переполнение и считать сначалаEOCD64Locator
, затемEOCD64
- для каждой структуры типа
Central directory file header
иLocal File Header
определить поля на переполнение и, если таковые присутствуют, то считать их значения изExtraField
Вот, в принципе, и вся наука. Вы не подумайте, что я нашел эту информацию тщательным изучением ZIP файлов, просто читайте спецификацию ZIP формата (на английском).
ZIP64ExtendInformation — поля про размер содержит всегда, исходя из спецификации.
Спасибо за статью!
Но всё равно пришлось лезть в апноту.
Например, чтобы прояснить последовательность структур и узнать, что eocd64Size = размеру EoCD64-12
Попутно подглядел, как разные архиваторы сжимают файл >4Гб
Как ни странно, 7zip вообще не использует EOCD64 и EOCD64locator!!!
Только ExtraFieldRecord+ZIP64ExtendInformation в LocalHeader и CentralDirectoryHeader.
WinRar использует все перечисленные в статье структуры.
При этом прекрасно понимает архивы, созданные 7Zip. И наоборот.
Архиваторы используют следующие последовательности структур при упаковке файлов, размером больше 4Гб:
7-zip : LocalHdr+Ext2+CompressedData+CentralDHdr+Ext1+EoCD
WinRar: LocalHdr+Ext2+CompressedData+CentralDHdr+Ext1+EoCD64+EoCDlocator+EoCD
Ext1 и Ext2 — это связка ExtraFieldRecord+ZIP64ExtendInformation.
Цифра показывает количество полей в структуре ZIP64ExtendInformation:
1) uint64_t uncompressedSize;
2) uint64_t uncompressedSize + uint64_t compressedSize;
Соответственно в LocalHeader оба размера файла забиты 0xFFFFFFFF, а в CentralDirFileHdr — только поле uncompressedSize.
Свои кривые архивы проверял 3 программами: 7Zip, WinRar, Far manager3 (плагин Arclite).
Самый нетерпимый к ошибкам в структурах оказался 7Zip (7z.exe t .zip).
Самым «совместимый» — Far3. Он прекрасно работает с файлами без CentralDirectory (хотя по стандарту она обязана существовать)
Winrar оказался терпимым к ошибкам в структурах EOCD64 и EOCD64locator.
Такие вот практические наблюдения.
Может быть кому-нибудь пригодятся. :)
Извиняюсь, что поздно отвечаю, но спасибо Вам за комментарии и подробный разбор! :)