Skip to content

Commit 8527d42

Browse files
committed
Wrap strings passed to libzip with zip_source_function_create()
Wrap strings passed to libzip with zip_source_function_create() instead of using zip_source_buffer_create(). This allows us to make the string writable, and simplifies memory management.
1 parent 6d62d87 commit 8527d42

File tree

7 files changed

+197
-36
lines changed

7 files changed

+197
-36
lines changed

ext/zip/config.m4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ if test "$PHP_ZIP" != "no"; then
4949
AC_DEFINE([HAVE_ZIP], [1],
5050
[Define to 1 if the PHP extension 'zip' is available.])
5151

52-
PHP_NEW_EXTENSION([zip], [php_zip.c zip_stream.c], [$ext_shared])
52+
PHP_NEW_EXTENSION([zip], [php_zip.c zip_source.c zip_stream.c], [$ext_shared])
5353
PHP_ADD_EXTENSION_DEP(zip, pcre)
5454

5555
PHP_SUBST([ZIP_SHARED_LIBADD])

ext/zip/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ if (PHP_ZIP != "no") {
88
(PHP_ZIP_SHARED && CHECK_LIB("libzip.lib", "zip", PHP_ZIP) ||
99
CHECK_LIB("libzip_a.lib", "zip", PHP_ZIP) && CHECK_LIB("libbz2_a.lib", "zip", PHP_ZIP) && CHECK_LIB("zlib_a.lib", "zip", PHP_ZIP) && CHECK_LIB("liblzma_a.lib", "zip", PHP_ZIP))
1010
) {
11-
EXTENSION('zip', 'php_zip.c zip_stream.c');
11+
EXTENSION('zip', 'php_zip.c zip_source.c zip_stream.c');
1212
ADD_EXTENSION_DEP('zip', 'pcre');
1313

1414
if (get_define("LIBS_ZIP").match("libzip_a(?:_debug)?\.lib")) {

ext/zip/php_zip.c

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -575,28 +575,6 @@ static char * php_zipobj_get_zip_comment(ze_zip_object *obj, int *len) /* {{{ */
575575
}
576576
/* }}} */
577577

578-
/* Add a string to the list of buffers to be released when the object is destroyed.*/
579-
static void php_zipobj_add_buffer(ze_zip_object *obj, zend_string *str) /* {{{ */
580-
{
581-
size_t pos = obj->buffers_cnt++;
582-
obj->buffers = safe_erealloc(obj->buffers, sizeof(*obj->buffers), obj->buffers_cnt, 0);
583-
obj->buffers[pos] = zend_string_copy(str);
584-
}
585-
/* }}} */
586-
587-
static void php_zipobj_release_buffers(ze_zip_object *obj) /* {{{ */
588-
{
589-
if (obj->buffers_cnt > 0) {
590-
for (size_t i = 0; i < obj->buffers_cnt; i++) {
591-
zend_string_release(obj->buffers[i]);
592-
}
593-
efree(obj->buffers);
594-
obj->buffers = NULL;
595-
}
596-
obj->buffers_cnt = 0;
597-
}
598-
/* }}} */
599-
600578
/* Close and free the zip_t */
601579
static bool php_zipobj_close(ze_zip_object *obj) /* {{{ */
602580
{
@@ -630,8 +608,6 @@ static bool php_zipobj_close(ze_zip_object *obj) /* {{{ */
630608
obj->filename_len = 0;
631609
}
632610

633-
php_zipobj_release_buffers(obj);
634-
635611
obj->za = NULL;
636612
return success;
637613
}
@@ -1531,10 +1507,12 @@ PHP_METHOD(ZipArchive, openString)
15311507

15321508
ze_zip_object *ze_obj = Z_ZIP_P(self);
15331509

1510+
php_zipobj_close(ze_obj);
1511+
15341512
zip_error_t err;
15351513
zip_error_init(&err);
15361514

1537-
zip_source_t * zip_source = zip_source_buffer_create(ZSTR_VAL(buffer), ZSTR_LEN(buffer), 0, &err);
1515+
zip_source_t * zip_source = php_zip_create_string_source(buffer, NULL, &err);
15381516

15391517
if (!zip_source) {
15401518
ze_obj->err_zip = zip_error_code_zip(&err);
@@ -1543,8 +1521,6 @@ PHP_METHOD(ZipArchive, openString)
15431521
RETURN_LONG(ze_obj->err_zip);
15441522
}
15451523

1546-
php_zipobj_close(ze_obj);
1547-
15481524
struct zip *intern = zip_open_from_source(zip_source, ZIP_RDONLY, &err);
15491525
if (!intern) {
15501526
ze_obj->err_zip = zip_error_code_zip(&err);
@@ -1554,7 +1530,6 @@ PHP_METHOD(ZipArchive, openString)
15541530
RETURN_LONG(ze_obj->err_zip);
15551531
}
15561532

1557-
php_zipobj_add_buffer(ze_obj, buffer);
15581533
ze_obj->za = intern;
15591534
zip_error_fini(&err);
15601535
RETURN_TRUE;
@@ -1597,7 +1572,7 @@ PHP_METHOD(ZipArchive, close)
15971572
}
15981573
/* }}} */
15991574

1600-
/* {{{ close the zip archive */
1575+
/* {{{ get the number of entries */
16011576
PHP_METHOD(ZipArchive, count)
16021577
{
16031578
struct zip *intern;
@@ -1911,9 +1886,7 @@ PHP_METHOD(ZipArchive, addFromString)
19111886
ZIP_FROM_OBJECT(intern, self);
19121887

19131888
ze_obj = Z_ZIP_P(self);
1914-
php_zipobj_add_buffer(ze_obj, buffer);
1915-
1916-
zs = zip_source_buffer(intern, ZSTR_VAL(buffer), ZSTR_LEN(buffer), 0);
1889+
zs = php_zip_create_string_source(buffer, NULL, NULL);
19171890

19181891
if (zs == NULL) {
19191892
RETURN_FALSE;

ext/zip/php_zip.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,9 @@ typedef struct _ze_zip_read_rsrc {
6868
/* Extends zend object */
6969
typedef struct _ze_zip_object {
7070
struct zip *za;
71-
zend_string **buffers;
7271
HashTable *prop_handler;
7372
char *filename;
7473
size_t filename_len;
75-
size_t buffers_cnt;
7674
zip_int64_t last_id;
7775
int err_zip;
7876
int err_sys;
@@ -96,6 +94,8 @@ php_stream *php_stream_zip_open(struct zip *arch, struct zip_stat *sb, const cha
9694

9795
extern const php_stream_wrapper php_stream_zip_wrapper;
9896

97+
zip_source_t * php_zip_create_string_source(zend_string *str, zend_string **dest, zip_error_t *err);
98+
9999
#define LIBZIP_ATLEAST(m,n,p) (((m<<16) + (n<<8) + p) <= ((LIBZIP_VERSION_MAJOR<<16) + (LIBZIP_VERSION_MINOR<<8) + LIBZIP_VERSION_MICRO))
100100

101101
#endif /* PHP_ZIP_H */

ext/zip/tests/checkcons.zip

181 Bytes
Binary file not shown.

ext/zip/tests/wrong-file-size.zip

110 Bytes
Binary file not shown.

ext/zip/zip_source.c

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
#ifdef HAVE_CONFIG_H
2+
# include "config.h"
3+
#endif
4+
#include "php.h"
5+
#include "php_zip.h"
6+
7+
typedef struct _php_zip_string_source {
8+
/* The current string being read from */
9+
zend_string *in_str;
10+
/* The offset into in_str of the current read position */
11+
size_t in_offset;
12+
/* The modification time returned in stat calls */
13+
time_t mtime;
14+
/* The current string being written to */
15+
zend_string *out_str;
16+
/* The offset into out_str of the current write position */
17+
size_t out_offset;
18+
/* A place to copy the result to when the archive is closed, or NULL */
19+
zend_string **dest;
20+
/* The error to be returned when libzip asks for the last error code */
21+
zip_error_t error;
22+
} php_zip_string_source;
23+
24+
static zip_int64_t php_zip_string_cb(void *userdata, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
25+
{
26+
php_zip_string_source *ctx = userdata;
27+
switch (cmd) {
28+
case ZIP_SOURCE_SUPPORTS:
29+
return zip_source_make_command_bitmap(
30+
ZIP_SOURCE_FREE,
31+
#if LIBZIP_VERSION_MAJOR > 1 || LIBZIP_VERSION_MINOR >= 10
32+
ZIP_SOURCE_SUPPORTS_REOPEN,
33+
#endif
34+
ZIP_SOURCE_OPEN,
35+
ZIP_SOURCE_READ,
36+
ZIP_SOURCE_CLOSE,
37+
ZIP_SOURCE_STAT,
38+
ZIP_SOURCE_ERROR,
39+
ZIP_SOURCE_SEEK,
40+
ZIP_SOURCE_TELL,
41+
ZIP_SOURCE_BEGIN_WRITE,
42+
ZIP_SOURCE_WRITE,
43+
ZIP_SOURCE_COMMIT_WRITE,
44+
ZIP_SOURCE_ROLLBACK_WRITE,
45+
ZIP_SOURCE_SEEK_WRITE,
46+
ZIP_SOURCE_TELL_WRITE,
47+
ZIP_SOURCE_REMOVE,
48+
-1
49+
);
50+
51+
case ZIP_SOURCE_FREE:
52+
zend_string_release(ctx->out_str);
53+
zend_string_release(ctx->in_str);
54+
efree(ctx);
55+
return 0;
56+
57+
/* Read ops */
58+
59+
case ZIP_SOURCE_OPEN:
60+
ctx->in_offset = 0;
61+
return 0;
62+
63+
case ZIP_SOURCE_READ: {
64+
size_t remaining = ZSTR_LEN(ctx->in_str) - ctx->in_offset;
65+
len = MIN(len, remaining);
66+
if (len) {
67+
memcpy(data, ZSTR_VAL(ctx->in_str) + ctx->in_offset, len);
68+
ctx->in_offset += len;
69+
}
70+
return len;
71+
}
72+
73+
case ZIP_SOURCE_CLOSE:
74+
return 0;
75+
76+
case ZIP_SOURCE_STAT: {
77+
zip_stat_t *st;
78+
if (len < sizeof(*st)) {
79+
zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
80+
return -1;
81+
}
82+
83+
st = (zip_stat_t *)data;
84+
zip_stat_init(st);
85+
st->mtime = ctx->mtime;
86+
st->size = ZSTR_LEN(ctx->in_str);
87+
st->comp_size = st->size;
88+
st->comp_method = ZIP_CM_STORE;
89+
st->encryption_method = ZIP_EM_NONE;
90+
st->valid = ZIP_STAT_MTIME | ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD | ZIP_STAT_ENCRYPTION_METHOD;
91+
92+
return sizeof(*st);
93+
}
94+
95+
case ZIP_SOURCE_ERROR:
96+
return zip_error_to_data(&ctx->error, data, len);
97+
98+
/* Seekable read ops */
99+
100+
case ZIP_SOURCE_SEEK: {
101+
zip_int64_t new_offset = zip_source_seek_compute_offset(
102+
ctx->in_offset, ZSTR_LEN(ctx->in_str), data, len, &ctx->error);
103+
if (new_offset < 0) {
104+
return -1;
105+
}
106+
ctx->in_offset = (size_t)new_offset;
107+
return 0;
108+
}
109+
110+
case ZIP_SOURCE_TELL:
111+
if (ctx->in_offset > ZIP_INT64_MAX) {
112+
zip_error_set(&ctx->error, ZIP_ER_TELL, EOVERFLOW);
113+
return -1;
114+
}
115+
return (zip_int64_t)ctx->in_offset;
116+
117+
/* Write ops */
118+
119+
case ZIP_SOURCE_BEGIN_WRITE:
120+
zend_string_release(ctx->out_str);
121+
ctx->out_str = ZSTR_EMPTY_ALLOC();
122+
return 0;
123+
124+
case ZIP_SOURCE_WRITE:
125+
if (ctx->out_offset > SIZE_MAX - len) {
126+
zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
127+
return -1;
128+
}
129+
if (ctx->out_offset + len > ZSTR_LEN(ctx->out_str)) {
130+
ctx->out_str = zend_string_realloc(ctx->out_str, ctx->out_offset + len, false);
131+
}
132+
memcpy(ZSTR_VAL(ctx->out_str) + ctx->out_offset, data, len);
133+
ctx->out_offset += len;
134+
return len;
135+
136+
case ZIP_SOURCE_COMMIT_WRITE:
137+
ZSTR_VAL(ctx->out_str)[ZSTR_LEN(ctx->out_str)] = '\0';
138+
zend_string_release(ctx->in_str);
139+
ctx->in_str = ctx->out_str;
140+
ctx->out_str = ZSTR_EMPTY_ALLOC();
141+
if (ctx->dest) {
142+
*(ctx->dest) = zend_string_copy(ctx->in_str);
143+
}
144+
return 0;
145+
146+
case ZIP_SOURCE_ROLLBACK_WRITE:
147+
zend_string_release(ctx->out_str);
148+
ctx->out_str = ZSTR_EMPTY_ALLOC();
149+
return 0;
150+
151+
case ZIP_SOURCE_SEEK_WRITE: {
152+
zip_int64_t new_offset = zip_source_seek_compute_offset(
153+
ctx->out_offset, ZSTR_LEN(ctx->out_str), data, len, &ctx->error);
154+
if (new_offset < 0) {
155+
return -1;
156+
}
157+
ctx->out_offset = new_offset;
158+
return 0;
159+
}
160+
161+
case ZIP_SOURCE_TELL_WRITE:
162+
if (ctx->out_offset > ZIP_INT64_MAX) {
163+
zip_error_set(&ctx->error, ZIP_ER_TELL, EOVERFLOW);
164+
return -1;
165+
}
166+
return (zip_int64_t)ctx->out_offset;
167+
168+
case ZIP_SOURCE_REMOVE:
169+
zend_string_release(ctx->in_str);
170+
ctx->in_str = ZSTR_EMPTY_ALLOC();
171+
ctx->in_offset = 0;
172+
return 0;
173+
174+
default:
175+
zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
176+
return -1;
177+
}
178+
}
179+
180+
zip_source_t * php_zip_create_string_source(zend_string *str, zend_string **dest, zip_error_t *err) /* {{{ */
181+
{
182+
php_zip_string_source *ctx = ecalloc(1, sizeof(php_zip_string_source));
183+
ctx->in_str = zend_string_copy(str);
184+
ctx->out_str = ZSTR_EMPTY_ALLOC();
185+
ctx->dest = dest;
186+
ctx->mtime = time(NULL);
187+
return zip_source_function_create(php_zip_string_cb, (void*)ctx, err);
188+
}

0 commit comments

Comments
 (0)