Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No freopen for append #223

Merged
merged 1 commit into from
Dec 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 145 additions & 1 deletion src/miniz.h
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,12 @@ MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip,
mz_uint flags,
mz_uint64 file_start_ofs,
mz_uint64 archive_size);
MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2_rpb(mz_zip_archive *pZip,
const char *pFilename,
mz_uint flags,
mz_uint64 file_start_ofs,
mz_uint64 archive_size);


/* Read an archive from an already opened FILE, beginning at the current file
* position. */
Expand Down Expand Up @@ -1619,6 +1625,9 @@ MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip,
MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip,
const char *pFilename,
mz_uint flags);
MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2_noreopen(mz_zip_archive *pZip,
const char *pFilename,
mz_uint flags);

/* Adds the contents of a memory buffer to an archive. These functions record
* the current local time into the archive. */
Expand Down Expand Up @@ -5966,6 +5975,59 @@ mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename,
return MZ_TRUE;
}

mz_bool mz_zip_reader_init_file_v2_rpb(mz_zip_archive *pZip, const char *pFilename,
mz_uint flags, mz_uint64 file_start_ofs,
mz_uint64 archive_size) {
mz_uint64 file_size;
MZ_FILE *pFile;

if ((!pZip) || (!pFilename) ||
((archive_size) &&
(archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);

pFile = MZ_FOPEN(pFilename, "r+b");
if (!pFile)
return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);

file_size = archive_size;
if (!file_size) {
if (MZ_FSEEK64(pFile, 0, SEEK_END)) {
MZ_FCLOSE(pFile);
return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);
}

file_size = MZ_FTELL64(pFile);
}

/* TODO: Better sanity check archive_size and the # of actual remaining bytes
*/

if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) {
MZ_FCLOSE(pFile);
return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
}

if (!mz_zip_reader_init_internal(pZip, flags)) {
MZ_FCLOSE(pFile);
return MZ_FALSE;
}

pZip->m_zip_type = MZ_ZIP_TYPE_FILE;
pZip->m_pRead = mz_zip_file_read_func;
pZip->m_pIO_opaque = pZip;
pZip->m_pState->m_pFile = pFile;
pZip->m_archive_size = file_size;
pZip->m_pState->m_file_archive_start_ofs = file_start_ofs;

if (!mz_zip_reader_read_central_dir(pZip, flags)) {
mz_zip_reader_end_internal(pZip, MZ_FALSE);
return MZ_FALSE;
}

return MZ_TRUE;
}

mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile,
mz_uint64 archive_size, mz_uint flags) {
mz_uint64 cur_file_ofs;
Expand Down Expand Up @@ -7992,6 +8054,88 @@ mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip,
return MZ_TRUE;
}

mz_bool mz_zip_writer_init_from_reader_v2_noreopen(mz_zip_archive *pZip,
const char *pFilename,
mz_uint flags) {
mz_zip_internal_state *pState;

if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);

if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) {
/* We don't support converting a non-zip64 file to zip64 - this seems like
* more trouble than it's worth. (What about the existing 32-bit data
* descriptors that could follow the compressed data?) */
if (!pZip->m_pState->m_zip64)
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
}

/* No sense in trying to write to an archive that's already at the support max
* size */
if (pZip->m_pState->m_zip64) {
if (pZip->m_total_files == MZ_UINT32_MAX)
return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
} else {
if (pZip->m_total_files == MZ_UINT16_MAX)
return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);

if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)
return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);
}

pState = pZip->m_pState;

if (pState->m_pFile) {
#ifdef MINIZ_NO_STDIO
(void)pFilename;
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
#else
if (pZip->m_pIO_opaque != pZip)
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);

if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) {
if (!pFilename)
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
}

pZip->m_pWrite = mz_zip_file_write_func;
pZip->m_pNeeds_keepalive = NULL;
#endif /* #ifdef MINIZ_NO_STDIO */
} else if (pState->m_pMem) {
/* Archive lives in a memory block. Assume it's from the heap that we can
* resize using the realloc callback. */
if (pZip->m_pIO_opaque != pZip)
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);

pState->m_mem_capacity = pState->m_mem_size;
pZip->m_pWrite = mz_zip_heap_write_func;
pZip->m_pNeeds_keepalive = NULL;
}
/* Archive is being read via a user provided read function - make sure the
user has specified a write function too. */
else if (!pZip->m_pWrite)
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);

/* Start writing new files at the archive's current central directory
* location. */
/* TODO: We could add a flag that lets the user start writing immediately
* AFTER the existing central dir - this would be safer. */
pZip->m_archive_size = pZip->m_central_directory_file_ofs;
pZip->m_central_directory_file_ofs = 0;

/* Clear the sorted central dir offsets, they aren't useful or maintained now.
*/
/* Even though we're now in write mode, files can still be extracted and
* verified, but file locates will be slow. */
/* TODO: We could easily maintain the sorted central directory offsets. */
mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets);

pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;

return MZ_TRUE;
}

mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip,
const char *pFilename) {
return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0);
Expand Down Expand Up @@ -8560,7 +8704,7 @@ mz_bool mz_zip_writer_add_read_buf_callback(

pState = pZip->m_pState;
cur_archive_file_ofs = pZip->m_archive_size;

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); */
Expand Down
13 changes: 7 additions & 6 deletions src/zip.c
Original file line number Diff line number Diff line change
Expand Up @@ -822,17 +822,18 @@ struct zip_t *zip_open(const char *zipname, int level, char mode) {
case 'r':
case 'a':
case 'd':
if (!mz_zip_reader_init_file(
if (!mz_zip_reader_init_file_v2_rpb(
&(zip->archive), zipname,
zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) {
// An archive file does not exist or cannot initialize
// zip_archive reader
goto cleanup;
}
if ((mode == 'a' || mode == 'd') &&
!mz_zip_writer_init_from_reader(&(zip->archive), zipname)) {
mz_zip_reader_end(&(zip->archive));
goto cleanup;
if ((mode == 'a' || mode == 'd')) {
if (!mz_zip_writer_init_from_reader_v2_noreopen(&(zip->archive), zipname, 0)) {
mz_zip_reader_end(&(zip->archive));
goto cleanup;
}
}
break;

Expand Down
8 changes: 7 additions & 1 deletion test/test_append.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ void test_setup(void) {
zip_close(zip);
}

void test_teardown(void) { remove(ZIPNAME); }
void test_teardown(void) {
remove(ZIPNAME);
}

#define TESTDATA2 "Some test data 2...\0"
#define CRC32DATA2 2532008468
Expand All @@ -41,15 +43,19 @@ MU_TEST(test_append) {
mu_check(CRC32DATA2 == zip_entry_crc32(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
++total_entries;
zip_close(zip);

zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
mu_assert_int_eq(0, zip_entry_open(zip, "test\\empty/"));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/empty/"));
mu_assert_int_eq(0, zip_entry_size(zip));
mu_assert_int_eq(0, zip_entry_crc32(zip));
mu_assert_int_eq(total_entries, zip_entry_index(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
++total_entries;
zip_close(zip);

zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
mu_assert_int_eq(0, zip_entry_open(zip, "empty/"));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "empty/"));
mu_assert_int_eq(0, zip_entry_size(zip));
Expand Down