Skip to content

Commit

Permalink
Open archive from C stream (FILE *) (#338)
Browse files Browse the repository at this point in the history
  • Loading branch information
kuba-- committed Feb 17, 2024
1 parent 358a929 commit 13b47cd
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 28 deletions.
19 changes: 3 additions & 16 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,6 @@ jobs:
- name: Test
run: cmake --build build --target test

# freebsd:
# runs-on: macos-latest
# steps:
# - uses: actions/checkout@v2
# - name: Build and Test
# uses: vmactions/freebsd-vm@v0.1.2
# with:
# usesh: true
# prepare: pkg install -y cmake tree lang/gcc
# run: |
# cmake .
# cmake --build .
# tree -sha .
# ctest -VV

windows-msvc:
runs-on: "windows-latest"
steps:
Expand All @@ -76,4 +61,6 @@ jobs:
cmake --build build
tree /a /f build
- name: Test
run: cmake --build build --target test
run: |
cd build
ctest -VV -C "Debug" --output-on-failure
111 changes: 100 additions & 11 deletions src/zip.c
Original file line number Diff line number Diff line change
Expand Up @@ -654,27 +654,28 @@ static ssize_t zip_entry_setbyindex(struct zip_t *zip,
return 0;
}

static ssize_t zip_mem_move(void *pBuf, size_t bufSize, const mz_uint64 to, const mz_uint64 from, const size_t length) {
static ssize_t zip_mem_move(void *pBuf, size_t bufSize, const mz_uint64 to,
const mz_uint64 from, const size_t length) {
uint8_t *dst = NULL, *src = NULL, *end = NULL;

if(!pBuf) {
if (!pBuf) {
return ZIP_EINVIDX;
}

end = (uint8_t *)pBuf + bufSize;

if(to > bufSize) {
if (to > bufSize) {
return ZIP_EINVIDX;
}

if(from > bufSize) {
if (from > bufSize) {
return ZIP_EINVIDX;
}

dst = (uint8_t *)pBuf + to;
src = (uint8_t *)pBuf + from;

if(((dst + length) > end) || ((src + length) > end)) {
if (((dst + length) > end) || ((src + length) > end)) {
return ZIP_EINVIDX;
}

Expand Down Expand Up @@ -719,11 +720,12 @@ static ssize_t zip_files_move(struct zip_t *zip, mz_uint64 writen_num,
while ((mz_int64)length > 0) {
move_count = (length >= page_size) ? page_size : length;

if(pState->m_pFile) {
n = zip_file_move(pState->m_pFile, writen_num, read_num, move_count, move_buf,
page_size);
} else if(pState->m_pMem) {
n = zip_mem_move(pState->m_pMem, pState->m_mem_size, writen_num, read_num, move_count);
if (pState->m_pFile) {
n = zip_file_move(pState->m_pFile, writen_num, read_num, move_count,
move_buf, page_size);
} else if (pState->m_pMem) {
n = zip_mem_move(pState->m_pMem, pState->m_mem_size, writen_num, read_num,
move_count);
} else {
return ZIP_ENOFILE;
}
Expand Down Expand Up @@ -869,7 +871,7 @@ static ssize_t zip_entries_delete_mark(struct zip_t *zip,
mz_zip_internal_state *pState = zip->archive.m_pState;
zip->archive.m_zip_mode = MZ_ZIP_MODE_WRITING;

if(pState->m_pFile) {
if (pState->m_pFile) {
if (MZ_FSEEK64(pState->m_pFile, 0, SEEK_SET)) {
CLEANUP(deleted_entry_flag_array);
return ZIP_ENOENT;
Expand Down Expand Up @@ -1887,6 +1889,93 @@ void zip_stream_close(struct zip_t *zip) {
}
}

struct zip_t *zip_cstream_open(FILE *stream, int level, char mode) {
int errnum = 0;
return zip_cstream_openwitherror(stream, level, mode, &errnum);
}

struct zip_t *zip_cstream_openwitherror(FILE *stream, int level, char mode,
int *errnum) {
struct zip_t *zip = NULL;
*errnum = 0;
if (!stream) {
// zip archive stream is NULL
*errnum = ZIP_ENOFILE;
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) {
// out of memory
*errnum = ZIP_EOOMEM;
goto cleanup;
}

zip->level = (mz_uint)level;
switch (mode) {
case 'w':
// Create a new archive.
if (!mz_zip_writer_init_cfile(&(zip->archive), stream,
MZ_ZIP_FLAG_WRITE_ZIP64)) {
// Cannot initialize zip_archive writer
*errnum = ZIP_EWINIT;
goto cleanup;
}
break;

case 'r':
if (!mz_zip_reader_init_cfile(
&(zip->archive), stream, 0,
zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
// An archive file does not exist or cannot initialize
// zip_archive reader
*errnum = ZIP_ERINIT;
goto cleanup;
}
break;

case 'a':
case 'd':
if (!mz_zip_reader_init_cfile(
&(zip->archive), stream, 0,
zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
// 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), NULL,
0)) {
*errnum = ZIP_EWRINIT;
mz_zip_reader_end(&(zip->archive));
goto cleanup;
}
}
break;

default:
*errnum = ZIP_EINVMODE;
goto cleanup;
}

return zip;

cleanup:
CLEANUP(zip);
return NULL;
}

void zip_cstream_close(struct zip_t *zip) { zip_close(zip); }

int zip_create(const char *zipname, const char *filenames[], size_t len) {
int err = 0;
size_t i;
Expand Down
44 changes: 44 additions & 0 deletions src/zip.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#define ZIP_H

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>

Expand Down Expand Up @@ -500,6 +501,49 @@ extern ZIP_EXPORT ssize_t zip_stream_copy(struct zip_t *zip, void **buf,
*/
extern ZIP_EXPORT void zip_stream_close(struct zip_t *zip);

/**
* Opens zip archive from existing FILE stream with compression level using the
* given mode. The stream will not be closed when calling zip_close.
*
* @param stream C FILE stream.
* @param level compression level (0-9 are the standard zlib-style levels).
* @param mode file access mode. This mode should be equivalent to the mode
* provided when opening the file.
* - '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_cstream_open(FILE *stream, int level,
char mode);

/**
* Opens zip archive from existing FILE stream with compression level using the
* given mode. The function additionally returns @param errnum - The stream will
* not be closed when calling zip_close.
*
* @param stream C FILE stream.
* @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_cstream_openwitherror(FILE *stream, int level, char mode, int *errnum);

/**
* Closes the zip archive, releases resources - always finalize.
* This function is an alias for zip_close function.
*
* @param zip zip archive handler.
*/
extern ZIP_EXPORT void zip_cstream_close(struct zip_t *zip);

/**
* Creates a new archive and puts files into a single zip archive.
*
Expand Down
36 changes: 36 additions & 0 deletions test/test_extract.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,47 @@ MU_TEST(test_extract_stream) {
fclose(fp);
}

MU_TEST(test_extract_cstream) {
struct buffer_t buf;
FILE *ZIPFILE = fopen(ZIPNAME, "r");

struct zip_t *zip = zip_cstream_open(ZIPFILE, 0, 'r');
mu_check(zip != NULL);

memset((void *)&buf, 0, sizeof(struct buffer_t));

mu_assert_int_eq(0, zip_entry_open(zip, "test/test-1.txt"));
mu_assert_int_eq(0, zip_entry_extract(zip, on_extract, &buf));
mu_assert_int_eq(strlen(TESTDATA1), buf.size);
mu_assert_int_eq(0, strncmp(buf.data, TESTDATA1, buf.size));
mu_assert_int_eq(0, zip_entry_close(zip));

free(buf.data);
buf.data = NULL;
buf.size = 0;

memset((void *)&buf, 0, sizeof(struct buffer_t));

mu_assert_int_eq(0, zip_entry_open(zip, "dotfiles/.test"));
mu_assert_int_eq(0, zip_entry_extract(zip, on_extract, &buf));
mu_assert_int_eq(strlen(TESTDATA2), buf.size);
mu_assert_int_eq(0, strncmp(buf.data, TESTDATA2, buf.size));
mu_assert_int_eq(0, zip_entry_close(zip));

free(buf.data);
buf.data = NULL;
buf.size = 0;
fclose(ZIPFILE);

zip_cstream_close(zip);
}

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

MU_RUN_TEST(test_extract);
MU_RUN_TEST(test_extract_stream);
MU_RUN_TEST(test_extract_cstream);
}

int main(int argc, char *argv[]) {
Expand Down
22 changes: 21 additions & 1 deletion test/test_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,31 @@ MU_TEST(test_stream_openwitherror) {
zip_stream_close(zip);
}

MU_TEST(test_cstream_openwitherror) {
int errnum;
FILE *ZIPFILE = NULL;

struct zip_t *zip = zip_cstream_openwitherror(
ZIPFILE, ZIP_DEFAULT_COMPRESSION_LEVEL, 'r', &errnum);
mu_check(zip == NULL);
mu_assert_int_eq(ZIP_ENOFILE, errnum);

ZIPFILE = fopen(ZIPNAME, "w");
zip = zip_cstream_openwitherror(ZIPFILE, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w',
&errnum);
mu_check(zip != NULL);
mu_assert_int_eq(0, errnum);

zip_cstream_close(zip);
fclose(ZIPFILE);
}

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

MU_RUN_TEST(test_openwitherror);
MU_RUN_TEST(test_stream_openwitherror);
MU_RUN_TEST(test_cstream_openwitherror);
}

#define UNUSED(x) (void)x
Expand All @@ -65,4 +85,4 @@ int main(int argc, char *argv[]) {
MU_RUN_SUITE(test_entry_suite);
MU_REPORT();
return MU_EXIT_CODE;
}
}

0 comments on commit 13b47cd

Please sign in to comment.