Modified streaming interface in miniz library to use ZIP64 extension only for files exciding the limits.

The modification is based on a preallocation space in the Extra field using a phony custom block in the Local file header. This space is eventually overwritten by the ZIP64 extension if the file exceeds 4GiB.
Also were fixed differences from PKZip specification in the streaming interface.
This commit is contained in:
Lukáš Hejl 2021-05-20 19:44:12 +02:00
parent 6bb2982b98
commit f50046b31b

View file

@ -5953,17 +5953,16 @@ static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64
static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date)
{
(void)pZip;
memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, (pZip->m_pState->m_zip64) ? 0x002D : (method ? 0x0014 : 0x0000));
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX));
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX));
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, (pZip->m_pState->m_zip64 || comp_size > MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, (pZip->m_pState->m_zip64 || uncomp_size > MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);
return MZ_TRUE;
@ -5975,10 +5974,12 @@ static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_
mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date,
mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
{
(void)pZip;
mz_zip_internal_state *pState = pZip->m_pState;
mz_bool is_zip64_needed = uncomp_size >= MZ_UINT32_MAX || comp_size >= MZ_UINT32_MAX;
memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_MADE_BY_OFS, is_zip64_needed ? 0x002D : (method ? 0x0014 : 0x0000));
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, is_zip64_needed ? 0x002D : (method ? 0x0014 : 0x0000));
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time);
@ -6675,6 +6676,15 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
return MZ_TRUE;
}
static mz_uint32 mz_zip_writer_preallocate_extra_data(mz_uint8 *pBuf, mz_uint32 extra_data_size)
{
mz_uint8 *pDst = pBuf;
MZ_WRITE_LE16(pDst + 0, 0x9999);
MZ_WRITE_LE16(pDst + 2, extra_data_size);
memset(pDst + 4, 0, extra_data_size);
return 2 * sizeof(mz_uint16) + extra_data_size;
}
mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged_context* pContext, const char* pArchive_name, mz_uint64 max_size, const MZ_TIME_T* pFile_time, const void* pComment, mz_uint16 comment_size, mz_uint level_and_flags,
const char* user_extra_data, mz_uint user_extra_data_len, const char* user_extra_data_central, mz_uint user_extra_data_central_len)
{
@ -6708,13 +6718,6 @@ mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged
pState = pZip->m_pState;
if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX))
{
/* Source file is too large for non-zip64 */
/*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */
pState->m_zip64 = MZ_TRUE;
}
/* We could support this, but why? */
if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
@ -6778,55 +6781,45 @@ mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged
MZ_ASSERT((pContext->cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
}
if (!pState->m_zip64 && (pContext->local_dir_header_ofs >= MZ_UINT32_MAX)) {
pState->m_zip64 = MZ_TRUE;
/*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */
}
MZ_CLEAR_OBJ(local_dir_header);
if (pState->m_zip64)
{
if (max_size >= MZ_UINT32_MAX || pContext->local_dir_header_ofs >= MZ_UINT32_MAX)
{
pContext->pExtra_data = pContext->extra_data;
pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, (max_size >= MZ_UINT32_MAX) ? &pContext->uncomp_size : NULL,
(max_size >= MZ_UINT32_MAX) ? &pContext->comp_size : NULL, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL);
}
pContext->pExtra_data = pContext->extra_data;
pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, &pContext->uncomp_size, &pContext->comp_size,
(pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL);
}
else if (!pState->m_zip64 && max_size > MZ_UINT32_MAX)
{
pContext->pExtra_data = pContext->extra_data;
pContext->extra_size = mz_zip_writer_preallocate_extra_data(pContext->extra_data, 2 * sizeof(mz_uint64));
}
if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)pContext->archive_name_size, (mz_uint16)(pContext->extra_size + user_extra_data_len), 0, 0, 0, pContext->method, pContext->gen_flags, pContext->dos_time, pContext->dos_date))
return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)pContext->archive_name_size, (mz_uint16)(pContext->extra_size + user_extra_data_len), 0, 0, 0, pContext->method, pContext->gen_flags, pContext->dos_time, pContext->dos_date))
return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
pContext->cur_archive_file_ofs += sizeof(local_dir_header);
pContext->cur_archive_file_ofs += sizeof(local_dir_header);
if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, pArchive_name, pContext->archive_name_size) != pContext->archive_name_size)
{
return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
}
if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, pArchive_name, pContext->archive_name_size) != pContext->archive_name_size)
{
return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
}
pContext->cur_archive_file_ofs += pContext->archive_name_size;
pContext->cur_archive_file_ofs += pContext->archive_name_size;
if (pState->m_zip64 || max_size >= MZ_UINT32_MAX) {
if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, pContext->extra_data, pContext->extra_size) != pContext->extra_size)
return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
pContext->cur_archive_file_ofs += pContext->extra_size;
}
else
{
if ((pContext->comp_size > MZ_UINT32_MAX) || (pContext->cur_archive_file_ofs > MZ_UINT32_MAX))
return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)pContext->archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, pContext->method, pContext->gen_flags, pContext->dos_time, pContext->dos_date))
return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
pContext->cur_archive_file_ofs += sizeof(local_dir_header);
if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, pArchive_name, pContext->archive_name_size) != pContext->archive_name_size)
{
return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
}
pContext->cur_archive_file_ofs += pContext->archive_name_size;
}
if (user_extra_data_len > 0)
{
@ -6898,17 +6891,40 @@ mz_bool mz_zip_writer_add_staged_finish(mz_zip_writer_staged_context *pContext)
pContext->pZip->m_pFree(pContext->pZip->m_pAlloc_opaque, pContext->pCompressor);
pContext->pCompressor = NULL;
// Rewrite preallocated phony custom block in local dir header by ZIP64 extension. Also, other values are adjusted in the header.
if (pContext->file_ofs >= MZ_UINT32_MAX || pContext->add_state.m_comp_size >= MZ_UINT32_MAX) {
mz_uint64 local_dir_header_ofs = pContext->local_dir_header_ofs;
pContext->pExtra_data = pContext->extra_data;
pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, &pContext->uncomp_size, &pContext->comp_size, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL);
mz_uint8 min_version[2] = {0x2D, 0x00};
mz_uint8 comp_uncomp_size[4] = {0xFF, 0xFF, 0xFF, 0xFF};
if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LDH_VERSION_NEEDED_OFS, min_version, sizeof(min_version)) != sizeof(min_version))
return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED);
if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_uncomp_size, sizeof(comp_uncomp_size)) != sizeof(comp_uncomp_size))
return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED);
if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, comp_uncomp_size, sizeof(comp_uncomp_size)) != sizeof(comp_uncomp_size))
return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED);
if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + pContext->archive_name_size, pContext->extra_data, pContext->extra_size) != pContext->extra_size)
return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED);
}
pContext->uncomp_size = pContext->file_ofs;
pContext->comp_size = pContext->add_state.m_comp_size;
pContext->cur_archive_file_ofs = pContext->add_state.m_cur_archive_file_ofs;
// Clean pContext->pExtra_data, pContext->extra_data and pContext->extra_size after calling mz_zip_writer_add_staged_open
pContext->pExtra_data = NULL;
pContext->extra_size = 0;
memset(pContext->extra_data, 0, sizeof(mz_uint8) * MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE);
{
mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64];
mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32;
MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID);
MZ_WRITE_LE32(local_dir_footer + 4, pContext->uncomp_crc32);
if (pContext->pExtra_data == NULL)
if (pContext->uncomp_size < MZ_UINT32_MAX && pContext->comp_size < MZ_UINT32_MAX)
{
if (pContext->comp_size > MZ_UINT32_MAX)
return mz_zip_set_error(pContext->pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
@ -6929,10 +6945,11 @@ mz_bool mz_zip_writer_add_staged_finish(mz_zip_writer_staged_context *pContext)
pContext->cur_archive_file_ofs += local_dir_footer_size;
}
if (pContext->pExtra_data != NULL)
if (pContext->file_ofs >= MZ_UINT32_MAX || pContext->add_state.m_comp_size >= MZ_UINT32_MAX)
{
pContext->pExtra_data = pContext->extra_data;
pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, (pContext->uncomp_size >= MZ_UINT32_MAX) ? &pContext->uncomp_size : NULL,
(pContext->uncomp_size >= MZ_UINT32_MAX) ? &pContext->comp_size : NULL, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL);
(pContext->comp_size >= MZ_UINT32_MAX) ? &pContext->comp_size : NULL, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL);
}
if (!mz_zip_writer_add_to_central_dir(pContext->pZip, pContext->pArchive_name, (mz_uint16)pContext->archive_name_size, pContext->pExtra_data, (mz_uint16)pContext->extra_size, pContext->pComment, pContext->comment_size,