mirror of
https://github.com/zlib-ng/minizip-ng
synced 2025-03-28 21:13:18 +00:00

Consolidated all the path building to use mz_path_combine. Fixed bug in minizip when using mz_zip_write_entry. Created structs in minizip and miniunz for keeping all options together. Fixed bug reading end of zip when using split stream. Fixed large file support in win32.
986 lines
34 KiB
C
Executable File
986 lines
34 KiB
C
Executable File
/* zip.c -- Zip manipulation
|
|
Version 2.0.0, October 4th, 2017
|
|
part of the MiniZip project
|
|
|
|
Copyright (C) 2010-2017 Nathan Moinvaziri
|
|
Modifications for AES, PKWARE disk spanning
|
|
https://github.com/nmoinvaz/minizip
|
|
Copyright (C) 2009-2010 Mathias Svensson
|
|
Modifications for Zip64 support
|
|
http://result42.com
|
|
Copyright (C) 1998-2010 Gilles Vollant
|
|
http://www.winimage.com/zLibDll/minizip.html
|
|
|
|
This program is distributed under the terms of the same license as zlib.
|
|
See the accompanying LICENSE file for the full text of the license.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "zlib.h"
|
|
|
|
#include "mz.h"
|
|
#include "mz_strm.h"
|
|
#ifdef HAVE_AES
|
|
# include "mz_strm_aes.h"
|
|
#endif
|
|
#ifdef HAVE_BZIP2
|
|
# include "mz_strm_bzip.h"
|
|
#endif
|
|
#ifdef HAVE_CRYPT
|
|
# include "mz_strm_crypt.h"
|
|
#endif
|
|
#ifdef HAVE_LZMA
|
|
# include "mz_strm_lzma.h"
|
|
#endif
|
|
#include "mz_strm_mem.h"
|
|
#include "mz_strm_zlib.h"
|
|
|
|
#include "mz_zip.h"
|
|
|
|
/***************************************************************************/
|
|
|
|
#define MZ_ZIP_SIZE_CD_ITEM (0x2e)
|
|
#define MZ_ZIP_SIZE_CD_LOCATOR64 (0x14)
|
|
#define MZ_ZIP_SIZE_LOCALHEADER (0x1e)
|
|
|
|
/***************************************************************************/
|
|
|
|
typedef struct mz_zip_s
|
|
{
|
|
mz_zip_file file_info;
|
|
mz_zip_crypt crypt_info;
|
|
mz_zip_compress compress_info;
|
|
|
|
void *stream; // main stream
|
|
void *cd_stream; // memory stream for central directory
|
|
void *compress_stream; // compression stream
|
|
void *crc32_stream; // crc32 stream
|
|
void *crypt_stream; // encryption stream
|
|
|
|
uint64_t pos_local_header; // offset of the local header of the file currently writing
|
|
uint64_t add_position_when_writting_offset;
|
|
uint64_t number_entry;
|
|
uint16_t entry_opened; // 1 if a file in the zip is currently writ.
|
|
uint32_t number_disk; // number of the current disk, used for spanning ZIP
|
|
uint32_t number_disk_with_CD; // number the the disk with central dir, used for spanning ZIP
|
|
#ifndef NO_ADDFILEINEXISTINGZIP
|
|
char *comment;
|
|
#endif
|
|
} mz_zip;
|
|
|
|
/***************************************************************************/
|
|
|
|
// Locate the central directory of a zip file (at the end, just before the global comment)
|
|
static int32_t mz_zip_search_cd(void *stream, uint64_t *central_pos)
|
|
{
|
|
uint8_t buf[1024 + 4];
|
|
uint64_t file_size = 0;
|
|
uint64_t back_read = 0;
|
|
uint64_t max_back = UINT16_MAX; // maximum size of global comment
|
|
uint32_t read_size = sizeof(buf);
|
|
uint64_t read_pos = 0;
|
|
uint32_t i = 0;
|
|
|
|
*central_pos = 0;
|
|
|
|
if (mz_stream_seek(stream, 0, MZ_STREAM_SEEK_END) != MZ_OK)
|
|
return MZ_STREAM_ERROR;
|
|
|
|
file_size = mz_stream_tell(stream);
|
|
|
|
if (max_back > file_size)
|
|
max_back = file_size;
|
|
|
|
while (back_read < max_back)
|
|
{
|
|
back_read += (sizeof(buf) - 4);
|
|
if (back_read > max_back)
|
|
back_read = max_back;
|
|
|
|
read_pos = file_size - back_read;
|
|
if (read_size > (file_size - read_pos))
|
|
read_size = (uint32_t)(file_size - read_pos);
|
|
|
|
if (mz_stream_seek(stream, read_pos, MZ_STREAM_SEEK_SET) != MZ_OK)
|
|
break;
|
|
if (mz_stream_read(stream, buf, read_size) != read_size)
|
|
break;
|
|
|
|
for (i = read_size - 3; (i--) > 0;)
|
|
{
|
|
if (((*(buf + i)) == (MZ_ZIP_MAGIC_ENDHEADER & 0xff)) &&
|
|
((*(buf + i + 1)) == (MZ_ZIP_MAGIC_ENDHEADER >> 8 & 0xff)) &&
|
|
((*(buf + i + 2)) == (MZ_ZIP_MAGIC_ENDHEADER >> 16 & 0xff)) &&
|
|
((*(buf + i + 3)) == (MZ_ZIP_MAGIC_ENDHEADER >> 24 & 0xff)))
|
|
{
|
|
*central_pos = read_pos + i;
|
|
return MZ_OK;
|
|
}
|
|
}
|
|
|
|
if (*central_pos != 0)
|
|
break;
|
|
}
|
|
|
|
return MZ_EXIST_ERROR;
|
|
}
|
|
|
|
// Locate the central directory 64 of a zip file (at the end, just before the global comment)
|
|
static int32_t mz_zip_search_zip64_cd(void *stream, const uint64_t end_central_offset, uint64_t *central_pos)
|
|
{
|
|
uint64_t offset = 0;
|
|
uint32_t value32 = 0;
|
|
int16_t err = MZ_OK;
|
|
|
|
|
|
*central_pos = 0;
|
|
|
|
// Zip64 end of central directory locator
|
|
err = mz_stream_seek(stream, end_central_offset - MZ_ZIP_SIZE_CD_LOCATOR64, MZ_STREAM_SEEK_SET);
|
|
// Read locator signature
|
|
if (err == MZ_OK)
|
|
{
|
|
err = mz_stream_read_uint32(stream, &value32);
|
|
if (value32 != MZ_ZIP_MAGIC_ENDLOCHEADER64)
|
|
err = MZ_FORMAT_ERROR;
|
|
}
|
|
// Number of the disk with the start of the zip64 end of central directory
|
|
if (err == MZ_OK)
|
|
err = mz_stream_read_uint32(stream, &value32);
|
|
// Relative offset of the zip64 end of central directory record8
|
|
if (err == MZ_OK)
|
|
err = mz_stream_read_uint64(stream, &offset);
|
|
// Total number of disks
|
|
if (err == MZ_OK)
|
|
err = mz_stream_read_uint32(stream, &value32);
|
|
// Goto end of central directory record
|
|
if (err == MZ_OK)
|
|
err = mz_stream_seek(stream, offset, MZ_STREAM_SEEK_SET);
|
|
// The signature
|
|
if (err == MZ_OK)
|
|
{
|
|
err = mz_stream_read_uint32(stream, &value32);
|
|
if (value32 != MZ_ZIP_MAGIC_ENDHEADER64)
|
|
err = MZ_FORMAT_ERROR;
|
|
}
|
|
|
|
if (err == MZ_OK)
|
|
*central_pos = offset;
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
extern void* ZEXPORT mz_zip_open(uint8_t open_existing, void *stream)
|
|
{
|
|
mz_zip *zip = NULL;
|
|
#ifndef NO_ADDFILEINEXISTINGZIP
|
|
uint64_t byte_before_the_zipfile = 0; // byte before the zip file, (>0 for sfx)
|
|
uint64_t size_central_dir = 0; // size of the central directory
|
|
uint64_t offset_central_dir = 0; // offset of start of central directory
|
|
uint64_t number_entry_CD = 0; // total number of entries in the central dir
|
|
uint64_t number_entry = 0;
|
|
uint64_t central_pos = 0;
|
|
uint64_t central_pos64 = 0;
|
|
uint16_t value16 = 0;
|
|
uint32_t value32 = 0;
|
|
uint64_t value64 = 0;
|
|
uint16_t comment_size = 0;
|
|
#endif
|
|
int16_t err = MZ_OK;
|
|
|
|
|
|
|
|
zip = (mz_zip*)malloc(sizeof(mz_zip));
|
|
if (zip == NULL)
|
|
return NULL;
|
|
|
|
memset(zip, 0, sizeof(mz_zip));
|
|
|
|
zip->stream = stream;
|
|
|
|
mz_stream_mem_create(&zip->cd_stream);
|
|
mz_stream_mem_set_grow(zip->cd_stream, 1);
|
|
mz_stream_mem_open(zip->cd_stream, NULL, MZ_STREAM_MODE_CREATE);
|
|
|
|
#ifndef NO_ADDFILEINEXISTINGZIP
|
|
// Add file in a zip file
|
|
if (open_existing)
|
|
{
|
|
// Read and cache central directory records
|
|
if (mz_zip_search_cd(zip->stream, ¢ral_pos) == MZ_OK)
|
|
{
|
|
// Read end of central directory info
|
|
if (mz_stream_seek(zip->stream, central_pos, MZ_STREAM_SEEK_SET) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
|
|
// The signature, already checked
|
|
if (mz_stream_read_uint32(zip->stream, &value32) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
// Number of this disk
|
|
if (mz_stream_read_uint16(zip->stream, &value16) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
zip->number_disk = value16;
|
|
// Number of the disk with the start of the central directory
|
|
if (mz_stream_read_uint16(zip->stream, &value16) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
zip->number_disk_with_CD = value16;
|
|
// Total number of entries in the central dir on this disk
|
|
number_entry = 0;
|
|
if (mz_stream_read_uint16(zip->stream, &value16) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
else
|
|
number_entry = value16;
|
|
// Total number of entries in the central dir
|
|
number_entry_CD = 0;
|
|
if (mz_stream_read_uint16(zip->stream, &value16) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
else
|
|
number_entry_CD = value16;
|
|
if (number_entry_CD != number_entry)
|
|
err = MZ_FORMAT_ERROR;
|
|
// Size of the central directory
|
|
size_central_dir = 0;
|
|
if (mz_stream_read_uint32(zip->stream, &value32) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
else
|
|
size_central_dir = value32;
|
|
// Offset of start of central directory with respect to the starting disk number
|
|
offset_central_dir = 0;
|
|
if (mz_stream_read_uint32(zip->stream, &value32) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
else
|
|
offset_central_dir = value32;
|
|
// Zipfile global comment length
|
|
if (mz_stream_read_uint16(zip->stream, &comment_size) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
|
|
if ((err == MZ_OK) && ((number_entry_CD == UINT16_MAX) || (offset_central_dir == UINT32_MAX)))
|
|
{
|
|
// Format should be Zip64, as the central directory or file size is too large
|
|
if (mz_zip_search_zip64_cd(zip->stream, central_pos, ¢ral_pos64) == MZ_OK)
|
|
{
|
|
central_pos = central_pos64;
|
|
|
|
if (mz_stream_seek(zip->stream, central_pos, MZ_STREAM_SEEK_SET) != 0)
|
|
err = MZ_STREAM_ERROR;
|
|
|
|
// The signature, already checked
|
|
if (mz_stream_read_uint32(zip->stream, &value32) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
// Size of zip64 end of central directory record
|
|
if (mz_stream_read_uint64(zip->stream, &value64) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
// Version made by
|
|
if (mz_stream_read_uint16(zip->stream, &value16) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
// Version needed to extract
|
|
if (mz_stream_read_uint16(zip->stream, &value16) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
// Number of this disk
|
|
if (mz_stream_read_uint32(zip->stream, &zip->number_disk) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
// Number of the disk with the start of the central directory
|
|
if (mz_stream_read_uint32(zip->stream, &zip->number_disk_with_CD) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
// Total number of entries in the central directory on this disk
|
|
if (mz_stream_read_uint64(zip->stream, &number_entry) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
// Total number of entries in the central directory
|
|
if (mz_stream_read_uint64(zip->stream, &number_entry_CD) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
if (number_entry_CD != number_entry)
|
|
err = MZ_FORMAT_ERROR;
|
|
// Size of the central directory
|
|
if (mz_stream_read_uint64(zip->stream, &size_central_dir) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
// Offset of start of central directory with respect to the starting disk number
|
|
if (mz_stream_read_uint64(zip->stream, &offset_central_dir) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
}
|
|
else
|
|
{
|
|
err = MZ_FORMAT_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (err == MZ_OK)
|
|
{
|
|
if (central_pos < offset_central_dir + size_central_dir)
|
|
err = MZ_FORMAT_ERROR;
|
|
}
|
|
|
|
if ((err == MZ_OK) && (comment_size > 0))
|
|
{
|
|
zip->comment = (char *)malloc(comment_size + 1);
|
|
if (zip->comment)
|
|
{
|
|
if (mz_stream_read(zip->stream, zip->comment, comment_size) != comment_size)
|
|
err = MZ_STREAM_ERROR;
|
|
zip->comment[comment_size] = 0;
|
|
}
|
|
}
|
|
|
|
if (err == MZ_OK)
|
|
{
|
|
byte_before_the_zipfile = central_pos - (offset_central_dir + size_central_dir);
|
|
zip->add_position_when_writting_offset = byte_before_the_zipfile;
|
|
|
|
// Store central directory in memory
|
|
if (mz_stream_seek(zip->stream, offset_central_dir + byte_before_the_zipfile, MZ_STREAM_SEEK_SET) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
if (mz_stream_copy(zip->cd_stream, zip->stream, (uint32_t)size_central_dir) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
|
|
zip->number_entry = number_entry_CD;
|
|
|
|
if (mz_stream_seek(zip->stream, offset_central_dir + byte_before_the_zipfile, MZ_STREAM_SEEK_SET) != MZ_OK)
|
|
err = MZ_STREAM_ERROR;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (err != MZ_OK)
|
|
{
|
|
mz_stream_close(zip->cd_stream);
|
|
mz_stream_delete(&zip->cd_stream);
|
|
|
|
#ifndef NO_ADDFILEINEXISTINGZIP
|
|
if (zip->comment)
|
|
free(zip->comment);
|
|
#endif
|
|
free(zip);
|
|
return NULL;
|
|
}
|
|
|
|
return zip;
|
|
}
|
|
|
|
extern int ZEXPORT mz_zip_get_global_comment(void *handle, const char **global_comment)
|
|
{
|
|
mz_zip *zip = NULL;
|
|
|
|
if (handle == NULL || global_comment == NULL)
|
|
return MZ_PARAM_ERROR;
|
|
zip = (mz_zip*)handle;
|
|
*global_comment = zip->comment;
|
|
return MZ_OK;
|
|
}
|
|
|
|
extern int ZEXPORT mz_zip_entry_open(void *handle, const mz_zip_file *file_info,
|
|
const mz_zip_compress *compress_info, const mz_zip_crypt *crypt_info)
|
|
{
|
|
mz_zip *zip = NULL;
|
|
int64_t disk_number = 0;
|
|
uint16_t filename_size = 0;
|
|
uint16_t version_needed = 0;
|
|
int16_t err = MZ_OK;
|
|
|
|
#if !defined(HAVE_CRYPT) && !defined(HAVE_AES)
|
|
if (password != NULL)
|
|
return MZ_PARAM_ERROR;
|
|
#endif
|
|
if (handle == NULL)
|
|
return MZ_PARAM_ERROR;
|
|
if (file_info == NULL || file_info->filename == NULL)
|
|
return MZ_PARAM_ERROR;
|
|
if (compress_info == NULL)
|
|
return MZ_PARAM_ERROR;
|
|
if (crypt_info == NULL)
|
|
return MZ_PARAM_ERROR;
|
|
|
|
switch (compress_info->method)
|
|
{
|
|
case MZ_COMPRESS_METHOD_RAW:
|
|
case MZ_COMPRESS_METHOD_DEFLATE:
|
|
#ifdef HAVE_BZIP2
|
|
case MZ_COMPRESS_METHOD_BZIP2:
|
|
#endif
|
|
#if HAVE_LZMA
|
|
case MZ_COMPRESS_METHOD_LZMA:
|
|
#endif
|
|
err = MZ_OK;
|
|
break;
|
|
default:
|
|
return MZ_PARAM_ERROR;
|
|
}
|
|
|
|
zip = (mz_zip*)handle;
|
|
|
|
if (zip->entry_opened == 1)
|
|
{
|
|
err = mz_zip_entry_close(handle);
|
|
if (err != MZ_OK)
|
|
return err;
|
|
}
|
|
|
|
memcpy(&zip->file_info, file_info, sizeof(mz_zip_file));
|
|
memcpy(&zip->crypt_info, crypt_info, sizeof(mz_zip_crypt));
|
|
memcpy(&zip->compress_info, compress_info, sizeof(mz_zip_compress));
|
|
|
|
zip->file_info.flag |= MZ_ZIP_FLAG_DATA_DESCRIPTOR;
|
|
#ifdef HAVE_LZMA
|
|
zip->file_info.flag |= MZ_ZIP_FLAG_LZMA_EOS_MARKER;
|
|
#endif
|
|
if ((zip->compress_info.level == 8) || (zip->compress_info.level == 9))
|
|
zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_MAX;
|
|
if (zip->compress_info.level == 2)
|
|
zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_FAST;
|
|
if (zip->compress_info.level == 1)
|
|
zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_SUPER_FAST;
|
|
|
|
if (zip->crypt_info.password != NULL)
|
|
zip->file_info.flag |= MZ_ZIP_FLAG_ENCRYPTED;
|
|
else
|
|
zip->file_info.flag &= ~MZ_ZIP_FLAG_ENCRYPTED;
|
|
|
|
filename_size = (uint16_t)strlen(zip->file_info.filename);
|
|
|
|
zip->pos_local_header = mz_stream_tell(zip->stream);
|
|
if (zip->pos_local_header >= UINT32_MAX)
|
|
zip->file_info.zip64 = 1;
|
|
|
|
if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
|
|
zip->number_disk = (uint32_t)disk_number;
|
|
|
|
// Write the local header
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, (uint32_t)MZ_ZIP_MAGIC_LOCALHEADER);
|
|
|
|
version_needed = 20;
|
|
if (zip->file_info.zip64)
|
|
version_needed = 45;
|
|
#ifdef HAVE_AES
|
|
if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (zip->crypt_info.aes))
|
|
version_needed = 51;
|
|
#endif
|
|
#ifdef HAVE_LZMA
|
|
if (zip->compress_info.method == MZ_COMPRESS_METHOD_LZMA)
|
|
version_needed = 63;
|
|
#endif
|
|
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint16(zip->stream, version_needed);
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint16(zip->stream, zip->file_info.flag);
|
|
if (err == MZ_OK)
|
|
{
|
|
#ifdef HAVE_AES
|
|
if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (zip->crypt_info.aes))
|
|
err = mz_stream_write_uint16(zip->stream, MZ_AES_METHOD);
|
|
else
|
|
#endif
|
|
err = mz_stream_write_uint16(zip->stream, zip->compress_info.method);
|
|
}
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, zip->file_info.dos_date);
|
|
|
|
// CRC & compressed size & uncompressed size is in data descriptor
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, 0); // crc 32
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, 0); // compressed size
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, 0); // uncompressed size
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint16(zip->stream, filename_size);
|
|
if (err == MZ_OK)
|
|
{
|
|
uint16_t extrafield_size = zip->file_info.extrafield_local_size;
|
|
#ifdef HAVE_AES
|
|
if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (zip->crypt_info.aes))
|
|
extrafield_size += 4 + 7;
|
|
#endif
|
|
if (zip->file_info.zip64)
|
|
extrafield_size += 4;
|
|
err = mz_stream_write_uint16(zip->stream, extrafield_size);
|
|
}
|
|
if (err == MZ_OK)
|
|
{
|
|
if (mz_stream_write(zip->stream, zip->file_info.filename, filename_size) != filename_size)
|
|
err = MZ_STREAM_ERROR;
|
|
}
|
|
if (err == MZ_OK)
|
|
{
|
|
if (mz_stream_write(zip->stream, zip->file_info.extrafield_local,
|
|
zip->file_info.extrafield_local_size) != zip->file_info.extrafield_local_size)
|
|
err = MZ_STREAM_ERROR;
|
|
}
|
|
// Add ZIP64 extra info header to central directory
|
|
if (zip->file_info.zip64)
|
|
{
|
|
mz_stream_write_uint16(zip->stream, 0x0001);
|
|
mz_stream_write_uint16(zip->stream, 0);
|
|
}
|
|
#ifdef HAVE_AES
|
|
// Write the AES extended info
|
|
if ((err == MZ_OK) && (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (zip->crypt_info.aes))
|
|
{
|
|
err = mz_stream_write_uint16(zip->stream, 0x9901);
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint16(zip->stream, 7);
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint16(zip->stream, MZ_AES_VERSION);
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint8(zip->stream, 'A');
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint8(zip->stream, 'E');
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint8(zip->stream, MZ_AES_ENCRYPTIONMODE);
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint16(zip->stream, zip->compress_info.method);
|
|
}
|
|
#endif
|
|
|
|
if ((err == Z_OK) && (zip->crypt_info.password != NULL))
|
|
{
|
|
#ifdef HAVE_AES
|
|
if (zip->crypt_info.aes)
|
|
{
|
|
mz_stream_aes_create(&zip->crypt_stream);
|
|
mz_stream_aes_set_password(zip->crypt_stream, zip->crypt_info.password);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef HAVE_CRYPT
|
|
|
|
uint8_t verify1 = 0;
|
|
uint8_t verify2 = 0;
|
|
|
|
// Info-ZIP modification to ZipCrypto format:
|
|
// If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time.
|
|
|
|
verify1 = (uint8_t)((zip->file_info.dos_date >> 16) & 0xff);
|
|
verify2 = (uint8_t)((zip->file_info.dos_date >> 8) & 0xff);
|
|
|
|
mz_stream_crypt_create(&zip->crypt_stream);
|
|
mz_stream_crypt_set_password(zip->crypt_stream, zip->crypt_info.password);
|
|
mz_stream_crypt_set_verify(zip->crypt_stream, verify1, verify2);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (err == MZ_OK)
|
|
{
|
|
if (zip->crypt_stream == NULL)
|
|
{
|
|
mz_stream_passthru_create(&zip->crypt_stream);
|
|
mz_stream_set_base(zip->crypt_stream, zip->stream);
|
|
}
|
|
else
|
|
{
|
|
mz_stream_set_base(zip->crypt_stream, zip->stream);
|
|
|
|
if (mz_stream_open(zip->crypt_stream, NULL, MZ_STREAM_MODE_WRITE) != MZ_OK)
|
|
err = MZ_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
|
|
if (err == MZ_OK)
|
|
{
|
|
if (zip->compress_info.method == 0) // raw
|
|
{
|
|
mz_stream_passthru_create(&zip->compress_stream);
|
|
mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
|
|
}
|
|
else
|
|
{
|
|
if (zip->compress_info.method == MZ_COMPRESS_METHOD_DEFLATE)
|
|
mz_stream_zlib_create(&zip->compress_stream);
|
|
#ifdef HAVE_BZIP2
|
|
else if (zip->compress_info.method == MZ_COMPRESS_METHOD_BZIP2)
|
|
mz_stream_bzip_create(&zip->compress_stream);
|
|
#endif
|
|
#ifdef HAVE_LZMA
|
|
else if (zip->compress_info.method == MZ_COMPRESS_METHOD_LZMA)
|
|
mz_stream_lzma_create(&zip->compress_stream);
|
|
#endif
|
|
else
|
|
err = MZ_PARAM_ERROR;
|
|
|
|
if (err == MZ_OK)
|
|
{
|
|
mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, zip->compress_info.level);
|
|
mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_WINDOW_BITS, zip->compress_info.window_bits);
|
|
mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_MEM_LEVEL, zip->compress_info.mem_level);
|
|
mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_STRATEGY, zip->compress_info.strategy);
|
|
|
|
mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
|
|
|
|
if (mz_stream_open(zip->compress_stream, NULL, MZ_STREAM_MODE_WRITE) != MZ_OK)
|
|
err = MZ_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (err == Z_OK)
|
|
{
|
|
mz_stream_crc32_create(&zip->crc32_stream);
|
|
mz_stream_set_base(zip->crc32_stream, zip->compress_stream);
|
|
|
|
if (mz_stream_open(zip->crc32_stream, NULL, MZ_STREAM_MODE_WRITE) != MZ_OK)
|
|
err = MZ_INTERNAL_ERROR;
|
|
}
|
|
|
|
if (err == Z_OK)
|
|
zip->entry_opened = 1;
|
|
|
|
return err;
|
|
}
|
|
|
|
extern int ZEXPORT mz_zip_entry_write(void *handle, const void *buf, uint32_t len)
|
|
{
|
|
mz_zip *zip = NULL;
|
|
|
|
if (handle == NULL)
|
|
return MZ_PARAM_ERROR;
|
|
|
|
zip = (mz_zip*)handle;
|
|
|
|
if (zip->entry_opened == 0)
|
|
return MZ_PARAM_ERROR;
|
|
|
|
return mz_stream_write(zip->crc32_stream, buf, len);
|
|
}
|
|
|
|
extern int ZEXPORT mz_zip_entry_close_raw(void *handle, uint64_t uncompressed_size, uint32_t crc32)
|
|
{
|
|
mz_zip *zip = NULL;
|
|
uint64_t compressed_size = 0;
|
|
uint16_t extrafield_size = 0;
|
|
uint16_t extrafield_zip64_size = 0;
|
|
uint16_t filename_size = 0;
|
|
uint16_t comment_size = 0;
|
|
uint16_t version_needed = 0;
|
|
int16_t err = MZ_OK;
|
|
|
|
if (handle == NULL)
|
|
return MZ_PARAM_ERROR;
|
|
|
|
zip = (mz_zip*)handle;
|
|
|
|
if (zip->entry_opened == 0)
|
|
return MZ_PARAM_ERROR;
|
|
|
|
mz_stream_close(zip->compress_stream);
|
|
|
|
if ((zip->compress_info.method != 0) || (uncompressed_size == 0))
|
|
{
|
|
crc32 = mz_stream_crc32_get_value(zip->crc32_stream);
|
|
|
|
mz_stream_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&uncompressed_size);
|
|
mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
|
|
}
|
|
|
|
if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
|
|
{
|
|
mz_stream_set_base(zip->crypt_stream, zip->stream);
|
|
|
|
err = mz_stream_close(zip->crypt_stream);
|
|
|
|
if ((zip->compress_info.method != 0) || (uncompressed_size == 0))
|
|
mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
|
|
|
|
mz_stream_delete(&zip->crypt_stream);
|
|
}
|
|
|
|
mz_stream_delete(&zip->compress_stream);
|
|
mz_stream_crc32_delete(&zip->crc32_stream);
|
|
|
|
// Write data descriptor
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_DATADESCRIPTOR);
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, crc32);
|
|
if (err == MZ_OK)
|
|
{
|
|
if (zip->file_info.zip64)
|
|
err = mz_stream_write_uint64(zip->stream, compressed_size);
|
|
else
|
|
err = mz_stream_write_uint32(zip->stream, (uint32_t)compressed_size);
|
|
}
|
|
if (err == MZ_OK)
|
|
{
|
|
if (zip->file_info.zip64)
|
|
err = mz_stream_write_uint64(zip->stream, uncompressed_size);
|
|
else
|
|
err = mz_stream_write_uint32(zip->stream, (uint32_t)uncompressed_size);
|
|
}
|
|
|
|
// Write central directory header
|
|
|
|
// Calculate extra field size
|
|
version_needed = 20;
|
|
extrafield_size = zip->file_info.extrafield_global_size;
|
|
if (zip->file_info.zip64)
|
|
{
|
|
version_needed = 45;
|
|
extrafield_size += 4;
|
|
if (uncompressed_size >= UINT32_MAX)
|
|
extrafield_zip64_size += 8;
|
|
if (compressed_size >= UINT32_MAX)
|
|
extrafield_zip64_size += 8;
|
|
if (zip->pos_local_header >= UINT32_MAX)
|
|
extrafield_zip64_size += 8;
|
|
extrafield_size += extrafield_zip64_size;
|
|
}
|
|
#ifdef HAVE_AES
|
|
if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (zip->crypt_info.aes))
|
|
{
|
|
version_needed = 51;
|
|
extrafield_size += 4 + 7;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_LZMA
|
|
if (zip->compress_info.method == MZ_COMPRESS_METHOD_LZMA)
|
|
version_needed = 63;
|
|
#endif
|
|
|
|
filename_size = (uint16_t)strlen(zip->file_info.filename);
|
|
if (zip->file_info.comment != NULL)
|
|
comment_size = (uint16_t)strlen(zip->file_info.comment);
|
|
|
|
mz_stream_write_uint32(zip->cd_stream, MZ_ZIP_MAGIC_CENTRALHEADER);
|
|
mz_stream_write_uint16(zip->cd_stream, zip->file_info.version_madeby);
|
|
mz_stream_write_uint16(zip->cd_stream, version_needed);
|
|
mz_stream_write_uint16(zip->cd_stream, zip->file_info.flag);
|
|
mz_stream_write_uint16(zip->cd_stream, zip->compress_info.method);
|
|
mz_stream_write_uint32(zip->cd_stream, zip->file_info.dos_date);
|
|
|
|
mz_stream_write_uint32(zip->cd_stream, crc32); // crc
|
|
if (compressed_size >= UINT32_MAX) // compr size
|
|
mz_stream_write_uint32(zip->cd_stream, UINT32_MAX);
|
|
else
|
|
mz_stream_write_uint32(zip->cd_stream, (uint32_t)compressed_size);
|
|
if (uncompressed_size >= UINT32_MAX) // uncompr size
|
|
mz_stream_write_uint32(zip->cd_stream, UINT32_MAX);
|
|
else
|
|
mz_stream_write_uint32(zip->cd_stream, (uint32_t)uncompressed_size);
|
|
|
|
mz_stream_write_uint16(zip->cd_stream, filename_size);
|
|
mz_stream_write_uint16(zip->cd_stream, extrafield_size);
|
|
mz_stream_write_uint16(zip->cd_stream, comment_size);
|
|
mz_stream_write_uint16(zip->cd_stream, (uint16_t)zip->number_disk); // disk nm start
|
|
mz_stream_write_uint16(zip->cd_stream, zip->file_info.internal_fa);
|
|
mz_stream_write_uint32(zip->cd_stream, zip->file_info.external_fa);
|
|
|
|
if (zip->pos_local_header >= UINT32_MAX)
|
|
mz_stream_write_uint32(zip->cd_stream, UINT32_MAX);
|
|
else
|
|
mz_stream_write_uint32(zip->cd_stream,
|
|
(uint32_t)(zip->pos_local_header - zip->add_position_when_writting_offset));
|
|
|
|
mz_stream_write(zip->cd_stream, zip->file_info.filename, filename_size);
|
|
mz_stream_write(zip->cd_stream, zip->file_info.extrafield_global, zip->file_info.extrafield_global_size);
|
|
|
|
// Add ZIP64 extra info header to central directory
|
|
if (zip->file_info.zip64)
|
|
{
|
|
mz_stream_write_uint16(zip->cd_stream, 0x0001);
|
|
mz_stream_write_uint16(zip->cd_stream, extrafield_zip64_size);
|
|
|
|
if (uncompressed_size >= UINT32_MAX)
|
|
mz_stream_write_uint64(zip->cd_stream, uncompressed_size);
|
|
if (compressed_size >= UINT32_MAX)
|
|
mz_stream_write_uint64(zip->cd_stream, compressed_size);
|
|
if (zip->pos_local_header >= UINT32_MAX)
|
|
mz_stream_write_uint64(zip->cd_stream, zip->pos_local_header);
|
|
}
|
|
|
|
#ifdef HAVE_AES
|
|
// Write AES extra info header to central directory
|
|
if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (zip->crypt_info.aes))
|
|
{
|
|
mz_stream_write_uint16(zip->cd_stream, 0x9901);
|
|
mz_stream_write_uint16(zip->cd_stream, 7);
|
|
|
|
mz_stream_write_uint16(zip->cd_stream, MZ_AES_VERSION);
|
|
mz_stream_write_uint8(zip->cd_stream, 'A');
|
|
mz_stream_write_uint8(zip->cd_stream, 'E');
|
|
mz_stream_write_uint8(zip->cd_stream, MZ_AES_ENCRYPTIONMODE);
|
|
mz_stream_write_uint16(zip->cd_stream, zip->compress_info.method);
|
|
}
|
|
#endif
|
|
// Write comment
|
|
if (zip->file_info.comment != NULL)
|
|
{
|
|
mz_stream_write(zip->cd_stream, zip->file_info.comment, comment_size);
|
|
free(zip->comment);
|
|
}
|
|
|
|
zip->number_entry += 1;
|
|
zip->entry_opened = 0;
|
|
|
|
return err;
|
|
}
|
|
|
|
extern int ZEXPORT mz_zip_entry_close(void *handle)
|
|
{
|
|
return mz_zip_entry_close_raw(handle, 0, 0);
|
|
}
|
|
|
|
extern int ZEXPORT mz_zip_close(void *handle, const char *global_comment, uint16_t version_madeby)
|
|
{
|
|
mz_zip *zip = NULL;
|
|
uint32_t size_centraldir = 0;
|
|
uint16_t comment_size = 0;
|
|
uint64_t centraldir_pos_inzip = 0;
|
|
uint64_t pos = 0;
|
|
uint64_t cd_pos = 0;
|
|
int64_t disk_number = 0;
|
|
int16_t err = MZ_OK;
|
|
|
|
if (handle == NULL)
|
|
return MZ_PARAM_ERROR;
|
|
zip = (mz_zip*)handle;
|
|
|
|
if (zip->entry_opened == 1)
|
|
{
|
|
err = mz_zip_entry_close(handle);
|
|
if (err != MZ_OK)
|
|
return err;
|
|
}
|
|
|
|
#ifndef NO_ADDFILEINEXISTINGZIP
|
|
if (global_comment == NULL)
|
|
global_comment = zip->comment;
|
|
#endif
|
|
|
|
if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
|
|
zip->number_disk_with_CD = (uint32_t)disk_number + 1;
|
|
if (mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1) != MZ_OK)
|
|
centraldir_pos_inzip = mz_stream_tell(zip->stream);
|
|
|
|
mz_stream_seek(zip->cd_stream, 0, MZ_STREAM_SEEK_END);
|
|
size_centraldir = (uint32_t)mz_stream_tell(zip->cd_stream);
|
|
mz_stream_seek(zip->cd_stream, 0, MZ_STREAM_SEEK_SET);
|
|
|
|
err = mz_stream_copy(zip->stream, zip->cd_stream, size_centraldir);
|
|
|
|
mz_stream_close(zip->cd_stream);
|
|
mz_stream_delete(&zip->cd_stream);
|
|
|
|
pos = centraldir_pos_inzip - zip->add_position_when_writting_offset;
|
|
|
|
// Write the ZIP64 central directory header
|
|
if (pos >= UINT32_MAX || zip->number_entry > UINT32_MAX)
|
|
{
|
|
uint64_t zip64_eocd_pos_inzip = mz_stream_tell(zip->stream);
|
|
|
|
err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER64);
|
|
|
|
// Size of this 'zip64 end of central directory'
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint64(zip->stream, (uint64_t)44);
|
|
// Version made by
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint16(zip->stream, version_madeby);
|
|
// Version needed
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint16(zip->stream, (uint16_t)45);
|
|
// Number of this disk
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, zip->number_disk_with_CD);
|
|
// Number of the disk with the start of the central directory
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, zip->number_disk_with_CD);
|
|
// Total number of entries in the central dir on this disk
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint64(zip->stream, zip->number_entry);
|
|
// Total number of entries in the central dir
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint64(zip->stream, zip->number_entry);
|
|
// Size of the central directory
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint64(zip->stream, (uint64_t)size_centraldir);
|
|
|
|
if (err == MZ_OK)
|
|
{
|
|
// Offset of start of central directory with respect to the starting disk number
|
|
cd_pos = centraldir_pos_inzip - zip->add_position_when_writting_offset;
|
|
err = mz_stream_write_uint64(zip->stream, cd_pos);
|
|
}
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDLOCHEADER64);
|
|
|
|
// Number of the disk with the start of the central directory
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, zip->number_disk_with_CD);
|
|
// Relative offset to the end of zip64 central directory
|
|
if (err == MZ_OK)
|
|
{
|
|
cd_pos = zip64_eocd_pos_inzip - zip->add_position_when_writting_offset;
|
|
err = mz_stream_write_uint64(zip->stream, cd_pos);
|
|
}
|
|
// Number of the disk with the start of the central directory
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, zip->number_disk_with_CD + 1);
|
|
}
|
|
|
|
// Write the central directory header
|
|
|
|
// Signature
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER);
|
|
// Number of this disk
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_disk_with_CD);
|
|
// Number of the disk with the start of the central directory
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_disk_with_CD);
|
|
// Total number of entries in the central dir on this disk
|
|
if (err == MZ_OK)
|
|
{
|
|
if (zip->number_entry >= UINT16_MAX)
|
|
err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
|
|
else
|
|
err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
|
|
}
|
|
// Total number of entries in the central dir
|
|
if (err == MZ_OK)
|
|
{
|
|
if (zip->number_entry >= UINT16_MAX)
|
|
err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
|
|
else
|
|
err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
|
|
}
|
|
// Size of the central directory
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint32(zip->stream, size_centraldir);
|
|
// Offset of start of central directory with respect to the starting disk number
|
|
if (err == MZ_OK)
|
|
{
|
|
cd_pos = centraldir_pos_inzip - zip->add_position_when_writting_offset;
|
|
if (pos >= UINT32_MAX)
|
|
err = mz_stream_write_uint32(zip->stream, UINT32_MAX);
|
|
else
|
|
err = mz_stream_write_uint32(zip->stream, (uint32_t)cd_pos);
|
|
}
|
|
|
|
// Write global comment
|
|
if (global_comment != NULL)
|
|
comment_size = (uint16_t)strlen(global_comment);
|
|
if (err == MZ_OK)
|
|
err = mz_stream_write_uint16(zip->stream, comment_size);
|
|
if (err == MZ_OK)
|
|
{
|
|
if (mz_stream_write(zip->stream, global_comment, comment_size) != comment_size)
|
|
err = MZ_STREAM_ERROR;
|
|
}
|
|
|
|
#ifndef NO_ADDFILEINEXISTINGZIP
|
|
if (zip->comment)
|
|
free(zip->comment);
|
|
#endif
|
|
free(zip);
|
|
|
|
return err;
|
|
}
|