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

Add zip_openwitherror and zip_stream_openwitherror #310

Merged
merged 2 commits into from
Oct 2, 2023
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
2 changes: 1 addition & 1 deletion src/miniz.h
Original file line number Diff line number Diff line change
Expand Up @@ -4986,7 +4986,7 @@ static int mz_mkdir(const char *pDirname) {

#else
#pragma message( \
"Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.")
"Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.")
#ifndef MINIZ_NO_TIME
#include <utime.h>
#endif
Expand Down
58 changes: 46 additions & 12 deletions src/zip.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ struct zip_entry_mark_t {
size_t lf_length;
};

static const char *const zip_errlist[30] = {
static const char *const zip_errlist[33] = {
NULL,
"not initialized\0",
"invalid entry name\0",
Expand Down Expand Up @@ -147,11 +147,14 @@ static const char *const zip_errlist[30] = {
"fseek error\0",
"fread error\0",
"fwrite error\0",
"cannot initialize reader\0",
"cannot initialize writer\0",
"cannot initialize writer from reader\0",
};

const char *zip_strerror(int errnum) {
errnum = -errnum;
if (errnum <= 0 || errnum >= 30) {
if (errnum <= 0 || errnum >= 33) {
return NULL;
}

Expand Down Expand Up @@ -354,8 +357,7 @@ static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir,
}

#if defined(_MSC_VER)
strncpy_s(&path[dirlen], filename_size, info.m_filename,
filename_size);
strncpy_s(&path[dirlen], filename_size, info.m_filename, filename_size);
#else
strncpy(&path[dirlen], info.m_filename, filename_size);
#endif
Expand All @@ -376,8 +378,9 @@ static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir,
defined(__MINGW32__)
#else
if (info.m_uncomp_size > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE ||
!mz_zip_reader_extract_to_mem_no_alloc(zip_archive, i, symlink_to,
MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE, 0, NULL, 0)) {
!mz_zip_reader_extract_to_mem_no_alloc(
zip_archive, i, symlink_to, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE, 0,
NULL, 0)) {
err = ZIP_EMEMNOALLOC;
goto out;
}
Expand Down Expand Up @@ -812,23 +815,35 @@ static ssize_t zip_entries_delete_mark(struct zip_t *zip,
}

struct zip_t *zip_open(const char *zipname, int level, char mode) {
int errnum = 0;
return zip_openwitherror(zipname, level, mode, &errnum);
}

struct zip_t *zip_openwitherror(const char *zipname, int level, char mode,
int *errnum) {
struct zip_t *zip = NULL;
*errnum = 0;

if (!zipname || strlen(zipname) < 1) {
// zip_t archive name is empty or NULL
*errnum = ZIP_EINVZIPNAME;
goto cleanup;
}

if (level < 0)
level = MZ_DEFAULT_LEVEL;
if ((level & 0xF) > MZ_UBER_COMPRESSION) {
// Wrong compression level
*errnum = ZIP_EINVLVL;
goto cleanup;
}

zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
if (!zip)
if (!zip) {
// out of memory
*errnum = ZIP_EOOMEM;
goto cleanup;
}

zip->level = (mz_uint)level;
switch (mode) {
Expand All @@ -837,6 +852,7 @@ struct zip_t *zip_open(const char *zipname, int level, char mode) {
if (!mz_zip_writer_init_file_v2(&(zip->archive), zipname, 0,
MZ_ZIP_FLAG_WRITE_ZIP64)) {
// Cannot initialize zip_archive writer
*errnum = ZIP_EWINIT;
goto cleanup;
}
break;
Expand All @@ -847,6 +863,7 @@ struct zip_t *zip_open(const char *zipname, int level, char mode) {
zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) {
// An archive file does not exist or cannot initialize
// zip_archive reader
*errnum = ZIP_ERINIT;
goto cleanup;
}
break;
Expand All @@ -858,18 +875,21 @@ struct zip_t *zip_open(const char *zipname, int level, char mode) {
zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) {
// An archive file does not exist or cannot initialize
// zip_archive reader
*errnum = ZIP_ERINIT;
goto cleanup;
}
if ((mode == 'a' || mode == 'd')) {
if (!mz_zip_writer_init_from_reader_v2_noreopen(&(zip->archive), zipname,
0)) {
*errnum = ZIP_EWRINIT;
mz_zip_reader_end(&(zip->archive));
goto cleanup;
}
}
break;

default:
*errnum = ZIP_EINVMODE;
goto cleanup;
}

Expand All @@ -886,16 +906,16 @@ void zip_close(struct zip_t *zip) {
// Always finalize, even if adding failed for some reason, so we have a
// valid central directory.
if (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) {
mz_zip_writer_finalize_archive(pZip);
mz_zip_writer_finalize_archive(pZip);
}

if (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING ||
if (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING ||
pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) {
zip_archive_truncate(pZip);
mz_zip_writer_end(pZip);
zip_archive_truncate(pZip);
mz_zip_writer_end(pZip);
}
if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) {
mz_zip_reader_end(pZip);
mz_zip_reader_end(pZip);
}

CLEANUP(zip);
Expand Down Expand Up @@ -1635,8 +1655,16 @@ int zip_stream_extract(const char *stream, size_t size, const char *dir,

struct zip_t *zip_stream_open(const char *stream, size_t size, int level,
char mode) {
int errnum = 0;
return zip_stream_openwitherror(stream, size, level, mode, &errnum);
}

struct zip_t *zip_stream_openwitherror(const char *stream, size_t size,
int level, char mode, int *errnum) {
struct zip_t *zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
if (!zip) {
// out of memory
*errnum = ZIP_EOOMEM;
return NULL;
}

Expand All @@ -1645,23 +1673,29 @@ struct zip_t *zip_stream_open(const char *stream, size_t size, int level,
}
if ((level & 0xF) > MZ_UBER_COMPRESSION) {
// Wrong compression level
*errnum = ZIP_EINVLVL;
goto cleanup;
}
zip->level = (mz_uint)level;

if ((stream != NULL) && (size > 0) && (mode == 'r')) {
if (!mz_zip_reader_init_mem(&(zip->archive), stream, size, 0)) {
*errnum = ZIP_ERINIT;
goto cleanup;
}
} else if ((stream == NULL) && (size == 0) && (mode == 'w')) {
// Create a new archive.
if (!mz_zip_writer_init_heap(&(zip->archive), 0, 1024)) {
// Cannot initialize zip_archive writer
*errnum = ZIP_EWINIT;
goto cleanup;
}
} else {
*errnum = ZIP_EINVMODE;
goto cleanup;
}

*errnum = 0;
return zip;

cleanup:
Expand Down
45 changes: 45 additions & 0 deletions src/zip.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ typedef long ssize_t; /* byte count or error */
#define ZIP_EFSEEK -27 // fseek error
#define ZIP_EFREAD -28 // fread error
#define ZIP_EFWRITE -29 // fwrite error
#define ZIP_ERINIT -30 // cannot initialize reader
#define ZIP_EWINIT -31 // cannot initialize writer
#define ZIP_EWRINIT -32 // cannot initialize writer from reader

/**
* Looks up the error message string corresponding to an error number.
Expand Down Expand Up @@ -124,6 +127,23 @@ struct zip_t;
extern ZIP_EXPORT struct zip_t *zip_open(const char *zipname, int level,
char mode);

/**
* Opens zip archive with compression level using the given mode.
* The function additionally returns @param errnum -
*
* @param zipname zip archive file name.
* @param level compression level (0-9 are the standard zlib-style levels).
* @param mode file access mode.
* - 'r': opens a file for reading/extracting (the file must exists).
* - 'w': creates an empty file for writing.
* - 'a': appends to an existing archive.
* @param errnum 0 on success, negative number (< 0) on error.
*
* @return the zip archive handler or NULL on error
*/
extern ZIP_EXPORT struct zip_t *
zip_openwitherror(const char *zipname, int level, char mode, int *errnum);

/**
* Closes the zip archive, releases resources - always finalize.
*
Expand Down Expand Up @@ -397,12 +417,37 @@ zip_stream_extract(const char *stream, size_t size, const char *dir,
*
* @param stream zip archive stream.
* @param size stream size.
* @param level compression level (0-9 are the standard zlib-style levels).
* @param mode file access mode.
* - 'r': opens a file for reading/extracting (the file must exists).
* - 'w': creates an empty file for writing.
* - 'a': appends to an existing archive.
*
* @return the zip archive handler or NULL on error
*/
extern ZIP_EXPORT struct zip_t *zip_stream_open(const char *stream, size_t size,
int level, char mode);

/**
* Opens zip archive stream into memory.
* The function additionally returns @param errnum -
*
* @param stream zip archive stream.
* @param size stream size.*
* @param level compression level (0-9 are the standard zlib-style levels).
* @param mode file access mode.
* - 'r': opens a file for reading/extracting (the file must exists).
* - 'w': creates an empty file for writing.
* - 'a': appends to an existing archive.
* @param errnum 0 on success, negative number (< 0) on error.
*
* @return the zip archive handler or NULL on error
*/
extern ZIP_EXPORT struct zip_t *zip_stream_openwitherror(const char *stream,
size_t size, int level,
char mode,
int *errnum);

/**
* Copy zip archive stream output buffer.
*
Expand Down
6 changes: 6 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@ add_executable(${test_permissions_out} test_permissions.c)
target_link_libraries(${test_permissions_out} zip)
add_test(NAME ${test_permissions_out} COMMAND ${test_permissions_out})
add_sanitizers(${test_permissions_out})

set(test_open_out test_open.out)
add_executable(${test_open_out} test_open.c)
target_link_libraries(${test_open_out} zip)
add_test(NAME ${test_open_out} COMMAND ${test_open_out})
add_sanitizers(${test_open_out})
62 changes: 62 additions & 0 deletions test/test_open.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include <stdio.h>
#include <stdlib.h>

#include <zip.h>

#include "minunit.h"

static char ZIPNAME[L_tmpnam + 1] = {0};

void test_setup(void) {
strncpy(ZIPNAME, "z-XXXXXX\0", L_tmpnam);
mktemp(ZIPNAME);
}

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

MU_TEST(test_openwitherror) {
int errnum;
struct zip_t *zip =
zip_openwitherror(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'r', &errnum);
mu_check(zip == NULL);
mu_assert_int_eq(ZIP_ERINIT, errnum);

zip = zip_openwitherror(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w', &errnum);
mu_check(zip != NULL);
mu_assert_int_eq(0, errnum);

zip_close(zip);
}

MU_TEST(test_stream_openwitherror) {
int errnum;
struct zip_t *zip = zip_stream_openwitherror(
NULL, 0, ZIP_DEFAULT_COMPRESSION_LEVEL, 'r', &errnum);
mu_check(zip == NULL);
mu_assert_int_eq(ZIP_EINVMODE, errnum);

zip = zip_stream_openwitherror(NULL, 0, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w',
&errnum);
mu_check(zip != NULL);
mu_assert_int_eq(0, errnum);

zip_stream_close(zip);
}

MU_TEST_SUITE(test_entry_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);

MU_RUN_TEST(test_openwitherror);
MU_RUN_TEST(test_stream_openwitherror);
}

#define UNUSED(x) (void)x

int main(int argc, char *argv[]) {
UNUSED(argc);
UNUSED(argv);

MU_RUN_SUITE(test_entry_suite);
MU_REPORT();
return MU_EXIT_CODE;
}
Loading