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

Open archive from C stream (FILE *) #338

Merged
merged 1 commit into from
Feb 17, 2024
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
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;
}
}
Loading