Я читаю CAB-файлы на PHP и столкнулся с проблемой при извлечении.
Вся структура кабины тщательно анализируется, но когда я сжимаюсь (методом deflate, rfc 1951), данные и gzinflate()
на нем происходит нечто странное:
Первый блок каждой папки распаковывается без каких-либо проблем, но если я пытаюсь распаковать любой из блоков N + 1, gzinflate завершается с ошибкой.
Я проверил, что скрипт правильно выбирает данные (я ищу CK
теги) и использует правильные смещения.
Чтобы понять проблему, позвольте мне описать формат cab и mszip. [увидеть больше на https://msdn.microsoft.com/en-us/library/bb417343.aspx]
CAB-файл содержит:
ФАЙЛ Структура имеет следующие поля:
ДАННЫЕ это сжатые данные порциями. Это также относится к конкретным папка и имеет следующие поля:
Для извлечения любого содержимого файла необходимо:
Читать и распаковывать блоки, в которых присутствует этот файл
+----------+----------+ +----------+----------+----------+
| block 0 | block 1 | | block 0 | block 1 | block 2 |
+----------+----------+ +----------+----------+----------+
=========== ================================
| file | | file 0 | file 1 |
=========== ================================
Соединить распакованные блоки
Это сжатые с помощью дефлята данные с 2-байтовым префиксом (CK
). Подробно: http://interoperability.blob.core.windows.net/files/MS-MCI/[MS-MCI].pdf
Одна вещь, которая может иметь значение, это: расшифровка
деревья ДОЛЖНЫ отбрасываться после каждого блока RFC 1951, но буфер истории ДОЛЖЕН поддерживаться. (7-я страница).
Я не знаю много о сжатии с дефляцией, но я использую для этого gzinflate. Это неправильно? Значит ли это, что я должен написать другую реализацию декомпрессора deflate?
https://github.com/wapmorgan/CabArchive/blob/master/src/CabArchive.php
Чтобы проверить распаковку любого блока, просто выполните:
<?php
$cab = new CabArchive('any-cab.cab');
// Next command decompress first MSZIP block - successfully
$cab->decompressBlocks(0, [0]);
// And this command failed with "data error"$cab->decompressBlocks(0, [1]);
Вам нужно передать последние 32 Кбайта несжатого (или все они, если меньше 32 Кбайт) на инфляцию следующей записи как предустановленный словарь. Функция PHP inflate_init
поддерживает это. Однако смотрите комментарии — inflate_init
может быть глючит
Также вам нужно предоставить все нули в качестве исходного словаря для первой инфляции.
Других решений пока нет …