diff --git a/src/zip.c b/src/zip.c index fb812a41..8712f434 100644 --- a/src/zip.c +++ b/src/zip.c @@ -490,6 +490,62 @@ static ssize_t zip_entry_mark(struct zip_t *zip, return err; } +static ssize_t zip_entry_markbyindex(struct zip_t *zip, + struct zip_entry_mark_t *entry_mark, + const ssize_t n, size_t entries[], + const size_t len) { + ssize_t i = 0; + ssize_t err = 0; + if (!zip || !entry_mark || !entries) { + return ZIP_ENOINIT; + } + + mz_zip_archive_file_stat file_stat; + mz_uint64 d_pos = UINT64_MAX; + for (i = 0; i < n; ++i) { + if ((err = zip_entry_openbyindex(zip, i))) { + return (ssize_t)err; + } + + mz_bool matches = MZ_FALSE; + { + size_t j; + for (j = 0; j < len; ++j) { + if ((size_t)i == entries[j]) { + matches = MZ_TRUE; + break; + } + } + } + if (matches) { + entry_mark[i].type = MZ_DELETE; + } else { + entry_mark[i].type = MZ_KEEP; + } + + if (!mz_zip_reader_file_stat(&zip->archive, i, &file_stat)) { + return ZIP_ENOENT; + } + + zip_entry_close(zip); + + entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs; + entry_mark[i].file_index = (ssize_t)-1; + entry_mark[i].lf_length = 0; + if ((entry_mark[i].type) == MZ_DELETE && + (d_pos > entry_mark[i].m_local_header_ofs)) { + d_pos = entry_mark[i].m_local_header_ofs; + } + } + + for (i = 0; i < n; ++i) { + if ((entry_mark[i].m_local_header_ofs > d_pos) && + (entry_mark[i].type != MZ_DELETE)) { + entry_mark[i].type = MZ_MOVE; + } + } + return err; +} static ssize_t zip_index_next(mz_uint64 *local_header_ofs_array, ssize_t cur_index) { ssize_t new_index = 0, i; @@ -583,6 +639,20 @@ static ssize_t zip_entry_set(struct zip_t *zip, return 0; } +static ssize_t zip_entry_setbyindex(struct zip_t *zip, + struct zip_entry_mark_t *entry_mark, ssize_t n, + size_t entries[], const size_t len) { + ssize_t err = 0; + + if ((err = zip_entry_markbyindex(zip, entry_mark, n, entries, len)) < 0) { + return err; + } + if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) { + return err; + } + return 0; +} + static ssize_t zip_file_move(MZ_FILE *m_pFile, const mz_uint64 to, const mz_uint64 from, const size_t length, mz_uint8 *move_buf, const size_t capacity_size) { @@ -1633,6 +1703,41 @@ ssize_t zip_entries_delete(struct zip_t *zip, char *const entries[], return err; } +ssize_t zip_entries_deletebyindex(struct zip_t *zip, size_t entries[], size_t len) +{ + ssize_t n = 0; + ssize_t err = 0; + struct zip_entry_mark_t *entry_mark = NULL; + + if (zip == NULL || (entries == NULL && len != 0)) { + return ZIP_ENOINIT; + } + + if (entries == NULL && len == 0) { + return 0; + } + + n = zip_entries_total(zip); + + entry_mark = (struct zip_entry_mark_t *)calloc( + (size_t)n, sizeof(struct zip_entry_mark_t)); + if (!entry_mark) { + return ZIP_EOOMEM; + } + + zip->archive.m_zip_mode = MZ_ZIP_MODE_READING; + + err = zip_entry_setbyindex(zip, entry_mark, n, entries, len); + if (err < 0) { + CLEANUP(entry_mark); + return err; + } + + err = zip_entries_delete_mark(zip, entry_mark, (int)n); + CLEANUP(entry_mark); + return err; +} + int zip_stream_extract(const char *stream, size_t size, const char *dir, int (*on_extract)(const char *filename, void *arg), void *arg) { diff --git a/src/zip.h b/src/zip.h index a08c16ab..fc1535cc 100644 --- a/src/zip.h +++ b/src/zip.h @@ -390,6 +390,16 @@ extern ZIP_EXPORT ssize_t zip_entries_total(struct zip_t *zip); extern ZIP_EXPORT ssize_t zip_entries_delete(struct zip_t *zip, char *const entries[], size_t len); +/** + * Deletes zip archive entries. + * + * @param zip zip archive handler. + * @param entries array of zip archive entries indices to be deleted. + * @param len the number of entries to be deleted. + * @return the number of deleted entries, or negative number (< 0) on error. + */ +extern ZIP_EXPORT ssize_t zip_entries_deletebyindex(struct zip_t *zip, size_t entries[], size_t len); + /** * Extracts a zip archive stream into directory. * diff --git a/test/test_entry.c b/test/test_entry.c index e829630d..e3059cf6 100644 --- a/test/test_entry.c +++ b/test/test_entry.c @@ -220,6 +220,106 @@ MU_TEST(test_list_entries) { zip_close(zip); } +MU_TEST(test_entries_deletebyindex) { + size_t entries[] = {5, 6, 7, 9, 8}; + + struct zip_t *zip = zip_open(ZIPNAME, 0, 'd'); + mu_check(zip != NULL); + + mu_assert_int_eq(5, zip_entries_deletebyindex(zip, entries, 5)); + + zip_close(zip); + + zip = zip_open(ZIPNAME, 0, 'r'); + mu_check(zip != NULL); + + mu_assert_int_eq(ZIP_ENOENT, zip_entry_open(zip, "delete.me")); + mu_assert_int_eq(0, zip_entry_close(zip)); + fprintf(stdout, "delete.me: %s\n", zip_strerror(ZIP_ENOENT)); + + mu_assert_int_eq(ZIP_ENOENT, zip_entry_open(zip, "_")); + mu_assert_int_eq(0, zip_entry_close(zip)); + fprintf(stdout, "_: %s\n", zip_strerror(ZIP_ENOENT)); + + mu_assert_int_eq(ZIP_ENOENT, zip_entry_open(zip, "delete/file.1")); + mu_assert_int_eq(0, zip_entry_close(zip)); + fprintf(stdout, "delete/file.1: %s\n", zip_strerror(ZIP_ENOENT)); + + mu_assert_int_eq(ZIP_ENOENT, zip_entry_open(zip, "deleteme/file.3")); + mu_assert_int_eq(0, zip_entry_close(zip)); + fprintf(stdout, "delete/file.3: %s\n", zip_strerror(ZIP_ENOENT)); + + mu_assert_int_eq(ZIP_ENOENT, zip_entry_open(zip, "delete/file.2")); + mu_assert_int_eq(0, zip_entry_close(zip)); + fprintf(stdout, "delete/file.2: %s\n", zip_strerror(ZIP_ENOENT)); + + mu_assert_int_eq(total_entries - 5, zip_entries_total(zip)); + + mu_assert_int_eq(0, zip_entry_open(zip, "delete/file.4")); + + size_t buftmp = 0; + char *buf = NULL; + ssize_t bufsize = zip_entry_read(zip, (void **)&buf, &buftmp); + + mu_assert_int_eq(bufsize, strlen(TESTDATA2)); + mu_assert_int_eq((size_t)bufsize, buftmp); + mu_assert_int_eq(0, strncmp(buf, TESTDATA2, bufsize)); + mu_assert_int_eq(0, zip_entry_close(zip)); + + free(buf); + buf = NULL; + + zip_close(zip); +} + + +MU_TEST(test_entries_deleteinvalid) { + size_t entries[] = {111, 222, 333, 444}; + + struct zip_t *zip = zip_open(ZIPNAME, 0, 'd'); + mu_check(zip != NULL); + + mu_assert_int_eq(0, zip_entries_deletebyindex(zip, entries, 4)); + + zip_close(zip); + + zip = zip_open(ZIPNAME, 0, 'r'); + mu_check(zip != NULL); + + mu_assert_int_eq(0, zip_entry_open(zip, "delete.me")); + mu_assert_int_eq(0, zip_entry_close(zip)); + + mu_assert_int_eq(0, zip_entry_open(zip, "_")); + mu_assert_int_eq(0, zip_entry_close(zip)); + + mu_assert_int_eq(0, zip_entry_open(zip, "delete/file.1")); + mu_assert_int_eq(0, zip_entry_close(zip)); + + mu_assert_int_eq(0, zip_entry_open(zip, "deleteme/file.3")); + mu_assert_int_eq(0, zip_entry_close(zip)); + + mu_assert_int_eq(0, zip_entry_open(zip, "delete/file.2")); + mu_assert_int_eq(0, zip_entry_close(zip)); + + mu_assert_int_eq(total_entries, zip_entries_total(zip)); + + mu_assert_int_eq(0, zip_entry_open(zip, "delete/file.4")); + + size_t buftmp = 0; + char *buf = NULL; + ssize_t bufsize = zip_entry_read(zip, (void **)&buf, &buftmp); + + mu_assert_int_eq(bufsize, strlen(TESTDATA2)); + mu_assert_int_eq((size_t)bufsize, buftmp); + mu_assert_int_eq(0, strncmp(buf, TESTDATA2, bufsize)); + mu_assert_int_eq(0, zip_entry_close(zip)); + + free(buf); + buf = NULL; + + zip_close(zip); +} + MU_TEST(test_entries_delete) { char *entries[] = {"delete.me", "_", "delete/file.1", "deleteme/file.3", "delete/file.2"}; @@ -282,6 +382,7 @@ MU_TEST_SUITE(test_entry_suite) { MU_RUN_TEST(test_entry_openbyindex); MU_RUN_TEST(test_entry_read); MU_RUN_TEST(test_list_entries); + MU_RUN_TEST(test_entries_deletebyindex); MU_RUN_TEST(test_entries_delete); }