zip_unpack_and_sparse.py
Этот скрипт позволяет распаковать zip-файл в случае, когда на диске хватит места для распакованных файлов, но не хватит для распакованных файлов и архива одновременно. Он распаковывает файл и затем стирает его из архива с помощью fallocate — таким образом файлы распаковываются занимаемое место (почти) не увеличивается, однако при этом архив уничтожается (иногда безвозвратно).
Хотя скрипт обмазан многочисленными проверками — он тестировался всего на двух архивах, так что никаких гарантий, на свой страх и риск, вот это вот всё.
Текущие ограничения: сжатие только Deflate, не поддерживаются зашифрованные архивы, права доступа игнорируются, пустые каталоги в архиве игнорируются. Можно допилить по пожеланиям.
Работа скрипта разделена на несколько этапов.
analyze
zip_unpack_and_sparse.py analyze [--check-crc] archive.zip info.json
Команда analyze считывает архив и записывает информацию об оффсетах файла и его заголовок в JSON-файлик.
Сжатие не применяется (но можно допилить, если хочется), так что при большом числе файлов такой JSON-файлик может весить десятки и сотни мегабайт.
Такой предварительный этап позволяет не зависеть от состояния zip-архива и восстановить его в случае если захочется вернуть всё взад.
Можно использовать опцию --check-crc
для проверки, что скрипт способен корректно считать файлы из архива, но это, разумеется, займёт дольше времени.
extract
zip_unpack_and_sparse.py extract [--decompress] [--grouped-sparse MiB] [--nosparse] info.json output_directory
Команда, которая собственно вытаскивает файлы из архива в указанный каталог. Он должен быть пустым или не существовать — скрипт его автоматически создать.
Путь к zip-архиву уже указан в JSON-файлике, поэтому указывать его не надо.
Если файлы расжимаются (--decompress
), то заодно проверяется CRC32-хэш, и об ошибках сообщается в консольку.
Опции такие:
--decompress
— по умолчанию файлы вытаскиваются сжатые (с расширением .zlib), но если вам нужно их расжать, то укажите эту опцию. Имейте в виду, что при расжатии файлов восстановить оригинальный zip-архив будет невозможно, так что делайте бэкапы.--grouped-sparse
— по умолчанию скрипт вызвает fallocate для каждого файла в отдельности, оставляя промежутки между ними нетронутыми, но прописывание числа мегабайт в этой опции включает сгруппированное обновление по указанному числу мегабайт, что может быть более эффективно. Имейте в виду, что эта опция также обнуляет заголовки файлов в zip-архиве, но в случае чего их можно будет восстановить из JSON-файлика (см. ниже)--nosparse
— никак не трогает оригинальный zip-архив, оставляя его в полном и неизменном виде (не вызывает fallocate). Эта опция только для тестирования, так как свободное место, очевидно, не появляется, и с её использованием скрипт становится бесполезен. Вы можете использовать эту опцию перед основным запуском, чтобы проверить, что распаковка идёт корректно, и прервать её (Ctrl+C), не дожидаясь завершения.
Если вы вытащили сжатые файлы (без опции --decompress
), их можно расжать вручную примерно такой командой:
(echo -en '\x78\xda' && cat myphoto.jpg.zlib) | zlib-flate -uncompress >myphoto.jpg
Команда echo
в данном случае добавляет zlib-заголовок. Я не обнаружил влияния разных заголовков на результат, но вот вам на всякий случай ссылочка на RFC 1950, который описывает формат этого заголовка.
restore
Если вы не использовали опцию --decompress
и у вас остались вытащенные сжатые файлы и JSON-файлик, вы можете восстановить zip-архив, записав файлы обратно в него.
zip_unpack_and_sparse.py restore info.json output_directory
Из указанного каталога будут читаться файлы и записываться в zip-архив, который прописан в JSON-файлике.
Применение опции --grouped-sparse
при распаковке стирает всё пространство между файлами, и если там кроме заголовков оказалось что-то ещё нужное (например, информация о пустых каталогах, которые игноируются скриптом), то zip-архив может не восстановиться, если вы использовали эту опцию. Тестирование на одном из архивов сделало восстановленный zip-архив читабельным, но md5-хэш не сошёлся с оригиналом из-за этих самых пустых папок.