Описание формата ZIP файла. Часть 2 (расширение ZIP64, поддержка больших файлов)

Давным-давно, когда создавался формат 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.

EOCD64Locator

struct EOCD64Locator
{
    // Обязательная сигнатура, равна 0x07064b50
    uint32_t signature;
    // Номер диска для поиска EOCD64
    uint32_t diskNumber;
    // Смещение от начала файла до EOCD64
    uint64_t eocd64Offset;
    // Количество дисков
    uint32_t totalDiskCount;
};

Определить, что наш ZIP файл содержит такую запись просто: проверьте поля стандартного End of central directory record. Если 16-битные поля равны 0xFFFF или 32-битные — 0xFFFFFFFF, значит такая запись должна быть.

ZIP64 End Of Central Directory Record

Эта структура представляет собой 64-битную версию стандартного End Of Central Directory Record. Находится по адресу EOCD64Locator.eocd64Offset.

EOCD64

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:

ExtraFieldRecord

struct ExtraFieldRecord
{
    // Заголовок (для ZIP64 extended information равен 0x0001)
    uint16_t headerId;
    // Размер данных
    uint16_t dataSize;
    // Какие-то данные размером dataSize
    uint8_t *data;
};

Данные ExtraFieldRecord.data для ZIP64 extended information выглядят так:

ZIP64ExtendInformation
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 формата (на английском).

Возможно, вас заинтересуют похожие посты:
3 комментария
  1. написал(а) Z (14 июня 2019, 08:16)

    ZIP64ExtendInformation — поля про размер содержит всегда, исходя из спецификации.

  2. написал(а) Vadim (24 июня 2020, 02:10)

    Спасибо за статью!
    Но всё равно пришлось лезть в апноту.
    Например, чтобы прояснить последовательность структур и узнать, что 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.

    Такие вот практические наблюдения.
    Может быть кому-нибудь пригодятся. :)

    1. написал(а) eJ (3 августа 2020, 16:21)

      Извиняюсь, что поздно отвечаю, но спасибо Вам за комментарии и подробный разбор! :)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Блог Евгения Жирнова