Skip to content

Commit

Permalink
keep original entry name (if open for reading) (#341)
Browse files Browse the repository at this point in the history
  • Loading branch information
kuba-- authored Mar 4, 2024
1 parent 13b47cd commit acb3fac
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 73 deletions.
119 changes: 53 additions & 66 deletions src/zip.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
/* Win32, DOS, MSVC, MSVS */
#include <direct.h>

#define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL)
#define HAS_DEVICE(P) \
((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \
(P)[1] == ':')
Expand All @@ -27,7 +26,6 @@
#else

#include <unistd.h> // needed for symlink()
#define STRCLONE(STR) ((STR) ? strdup(STR) : NULL)

#endif

Expand Down Expand Up @@ -215,6 +213,22 @@ static int zip_mkpath(char *path) {
return 0;
}

static char *zip_strclone(const char *str, size_t n) {
char c;
size_t i;
char *rpl = (char *)calloc((1 + n), sizeof(char));
char *begin = rpl;
if (!rpl) {
return NULL;
}

for (i = 0; (i < n) && (c = *str++); ++i) {
*rpl++ = c;
}

return begin;
}

static char *zip_strrpl(const char *str, size_t n, char oldchar, char newchar) {
char c;
size_t i;
Expand All @@ -235,6 +249,8 @@ static char *zip_strrpl(const char *str, size_t n, char oldchar, char newchar) {
}

static char *zip_name_normalize(char *name, char *const nname, size_t len) {
const char *const dot = ".\0";
const char *const dot2 = "..\0";
size_t offn = 0;
size_t offnn = 0, ncpy = 0;

Expand All @@ -247,8 +263,8 @@ static char *zip_name_normalize(char *name, char *const nname, size_t len) {

for (; offn < len; offn++) {
if (ISSLASH(name[offn])) {
if (ncpy > 0 && strcmp(&nname[offnn], ".\0") &&
strcmp(&nname[offnn], "..\0")) {
if (ncpy > 0 && strcmp(&nname[offnn], dot) &&
strcmp(&nname[offnn], dot2)) {
offnn += ncpy;
nname[offnn++] = name[offn]; // append '/'
}
Expand All @@ -260,31 +276,13 @@ static char *zip_name_normalize(char *name, char *const nname, size_t len) {
}

// at the end, extra check what we've already copied
if (ncpy == 0 || !strcmp(&nname[offnn], ".\0") ||
!strcmp(&nname[offnn], "..\0")) {
if (ncpy == 0 || !strcmp(&nname[offnn], dot) ||
!strcmp(&nname[offnn], dot2)) {
nname[offnn] = 0;
}
return nname;
}

static mz_bool zip_name_match(const char *name1, const char *name2) {
char *nname2 = NULL;

#ifdef ZIP_RAW_ENTRYNAME
nname2 = STRCLONE(name2);
#else
nname2 = zip_strrpl(name2, strlen(name2), '\\', '/');
#endif

if (!nname2) {
return MZ_FALSE;
}

mz_bool res = (strcmp(name1, nname2) == 0) ? MZ_TRUE : MZ_FALSE;
CLEANUP(nname2);
return res;
}

static int zip_archive_truncate(mz_zip_archive *pzip) {
mz_zip_internal_state *pState = pzip->m_pState;
mz_uint64 file_size = pzip->m_archive_size;
Expand Down Expand Up @@ -454,7 +452,7 @@ static ssize_t zip_entry_mark(struct zip_t *zip,
{
size_t j;
for (j = 0; j < len; ++j) {
if (zip_name_match(zip->entry.name, entries[j])) {
if (strcmp(zip->entry.name, entries[j]) == 0) {
name_matches = MZ_TRUE;
break;
}
Expand Down Expand Up @@ -959,6 +957,7 @@ struct zip_t *zip_openwitherror(const char *zipname, int level, char mode,
}

zip->level = (mz_uint)level;
zip->entry.index = -1;
switch (mode) {
case 'w':
// Create a new archive.
Expand Down Expand Up @@ -1071,33 +1070,18 @@ static int _zip_entry_open(struct zip_t *zip, const char *entryname,
return ZIP_EINVENTNAME;
}

/*
.ZIP File Format Specification Version: 6.3.3
4.4.17.1 The name of the file, with optional relative path.
The path stored MUST not contain a drive or
device letter, or a leading slash. All slashes
MUST be forward slashes '/' as opposed to
backwards slashes '\' for compatibility with Amiga
and UNIX file systems etc. If input came from standard
input, there is no file name field.
*/
if (zip->entry.name) {
CLEANUP(zip->entry.name);
}
#ifdef ZIP_RAW_ENTRYNAME
zip->entry.name = STRCLONE(entryname);
#else
zip->entry.name = zip_strrpl(entryname, entrylen, '\\', '/');
#endif

if (!zip->entry.name) {
// Cannot parse zip entry name
return ZIP_EINVENTNAME;
}

pzip = &(zip->archive);
if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) {
zip->entry.name = zip_strclone(entryname, entrylen);
if (!zip->entry.name) {
// Cannot parse zip entry name
return ZIP_EINVENTNAME;
}

zip->entry.index = (ssize_t)mz_zip_reader_locate_file(
pzip, zip->entry.name, NULL,
case_sensitive ? MZ_ZIP_FLAG_CASE_SENSITIVE : 0);
Expand Down Expand Up @@ -1125,6 +1109,23 @@ static int _zip_entry_open(struct zip_t *zip, const char *entryname,
return 0;
}

/*
.ZIP File Format Specification Version: 6.3.3
4.4.17.1 The name of the file, with optional relative path.
The path stored MUST not contain a drive or
device letter, or a leading slash. All slashes
MUST be forward slashes '/' as opposed to
backwards slashes '\' for compatibility with Amiga
and UNIX file systems etc. If input came from standard
input, there is no file name field.
*/
zip->entry.name = zip_strrpl(entryname, entrylen, '\\', '/');
if (!zip->entry.name) {
// Cannot parse zip entry name
return ZIP_EINVENTNAME;
}

level = zip->level & 0xF;

zip->entry.index = (ssize_t)zip->archive.m_total_files;
Expand Down Expand Up @@ -1288,26 +1289,11 @@ int zip_entry_openbyindex(struct zip_t *zip, size_t index) {
namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;

/*
.ZIP File Format Specification Version: 6.3.3
4.4.17.1 The name of the file, with optional relative path.
The path stored MUST not contain a drive or
device letter, or a leading slash. All slashes
MUST be forward slashes '/' as opposed to
backwards slashes '\' for compatibility with Amiga
and UNIX file systems etc. If input came from standard
input, there is no file name field.
*/
if (zip->entry.name) {
CLEANUP(zip->entry.name);
}
#ifdef ZIP_RAW_ENTRYNAME
zip->entry.name = STRCLONE(pFilename);
#else
zip->entry.name = zip_strrpl(pFilename, namelen, '\\', '/');
#endif

zip->entry.name = zip_strclone(pFilename, namelen);
if (!zip->entry.name) {
// local entry name is NULL
return ZIP_EINVENTNAME;
Expand Down Expand Up @@ -1397,7 +1383,7 @@ int zip_entry_close(struct zip_t *zip) {
(zip->entry.header_offset >= MZ_UINT32_MAX) ? &zip->entry.header_offset
: NULL);

if ((entrylen) && (zip->entry.name[entrylen - 1] == '/') &&
if ((entrylen) && ISSLASH(zip->entry.name[entrylen - 1]) &&
!zip->entry.uncomp_size) {
/* Set DOS Subdirectory attribute bit. */
zip->entry.external_attr |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG;
Expand All @@ -1422,6 +1408,7 @@ int zip_entry_close(struct zip_t *zip) {
cleanup:
if (zip) {
zip->entry.m_time = 0;
zip->entry.index = -1;
CLEANUP(zip->entry.name);
}
return err;
Expand All @@ -1432,7 +1419,6 @@ const char *zip_entry_name(struct zip_t *zip) {
// zip_t handler is not initialized
return NULL;
}

return zip->entry.name;
}

Expand All @@ -1446,6 +1432,7 @@ ssize_t zip_entry_index(struct zip_t *zip) {
}

int zip_entry_isdir(struct zip_t *zip) {
mz_uint16 entrylen;
if (!zip) {
// zip_t handler is not initialized
return ZIP_ENOINIT;
Expand All @@ -1456,8 +1443,8 @@ int zip_entry_isdir(struct zip_t *zip) {
return ZIP_EINVIDX;
}

return (int)mz_zip_reader_is_file_a_directory(&zip->archive,
(mz_uint)zip->entry.index);
entrylen = (mz_uint16)strlen(zip->entry.name);
return ISSLASH(zip->entry.name[entrylen - 1]);
}

unsigned long long zip_entry_size(struct zip_t *zip) {
Expand Down
12 changes: 7 additions & 5 deletions test/test_entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,12 @@ MU_TEST(test_entry_name) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);

mu_check(zip_entry_name(zip) == NULL);
mu_assert_int_eq(0, zip_entry_open(zip, "test/test-1.txt"));
const char *name = zip_entry_name(zip);
mu_check(NULL != name);

mu_assert_int_eq(0, zip_entry_open(zip, "test\\test-1.txt"));
mu_check(NULL != zip_entry_name(zip));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/test-1.txt"));
const char *name2 = "test/test-1.txt";
mu_assert_int_eq(0, strcmp(name, name2));
mu_assert_int_eq(strlen(TESTDATA1), zip_entry_size(zip));
mu_check(CRC32DATA1 == zip_entry_crc32(zip));
mu_assert_int_eq(0, zip_entry_index(zip));
Expand Down Expand Up @@ -141,7 +142,7 @@ MU_TEST(test_entry_index) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);

mu_assert_int_eq(0, zip_entry_open(zip, "test\\test-1.txt"));
mu_assert_int_eq(0, zip_entry_open(zip, "test/test-1.txt"));
mu_assert_int_eq(0, zip_entry_index(zip));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/test-1.txt"));
mu_assert_int_eq(strlen(TESTDATA1), zip_entry_size(zip));
Expand Down Expand Up @@ -399,6 +400,7 @@ MU_TEST(test_entry_offset) {

mu_assert_int_eq(off, zip_entry_header_offset(zip));

printf("\n'%s'\n", zip_entry_name(zip));
off = zip_entry_header_offset(zip) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE +
strlen(zip_entry_name(zip)) + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE +
zip_entry_comp_size(zip);
Expand Down
4 changes: 2 additions & 2 deletions test/test_read.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ MU_TEST(test_read) {
mu_check(zip != NULL);
mu_assert_int_eq(1, zip_is64(zip));

mu_assert_int_eq(0, zip_entry_open(zip, "test\\test-1.txt"));
mu_assert_int_eq(0, zip_entry_open(zip, "test/test-1.txt"));
mu_assert_int_eq(strlen(TESTDATA1), zip_entry_size(zip));
mu_check(CRC32DATA1 == zip_entry_crc32(zip));
bufsize = zip_entry_read(zip, (void **)&buf, &buftmp);
Expand All @@ -78,7 +78,7 @@ MU_TEST(test_read) {
free(buf);
buf = NULL;

mu_assert_int_eq(0, zip_entry_open(zip, "test\\empty/"));
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));
Expand Down

0 comments on commit acb3fac

Please sign in to comment.