1 /*
2 +----------------------------------------------------------------------+
3 | ZIP archive support for Phar |
4 +----------------------------------------------------------------------+
5 | Copyright (c) The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt. |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Gregory Beaver <cellog@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "phar_internal.h"
20
21 #define PHAR_GET_16(var) ((uint16_t)((((uint16_t)var[0]) & 0xff) | \
22 (((uint16_t)var[1]) & 0xff) << 8))
23 #define PHAR_GET_32(var) ((uint32_t)((((uint32_t)var[0]) & 0xff) | \
24 (((uint32_t)var[1]) & 0xff) << 8 | \
25 (((uint32_t)var[2]) & 0xff) << 16 | \
26 (((uint32_t)var[3]) & 0xff) << 24))
phar_write_32(char buffer[4],uint32_t value)27 static inline void phar_write_32(char buffer[4], uint32_t value)
28 {
29 buffer[3] = (unsigned char) ((value & 0xff000000) >> 24);
30 buffer[2] = (unsigned char) ((value & 0xff0000) >> 16);
31 buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
32 buffer[0] = (unsigned char) (value & 0xff);
33 }
phar_write_16(char buffer[2],uint32_t value)34 static inline void phar_write_16(char buffer[2], uint32_t value)
35 {
36 buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
37 buffer[0] = (unsigned char) (value & 0xff);
38 }
39 # define PHAR_SET_32(var, value) phar_write_32(var, (uint32_t) (value));
40 # define PHAR_SET_16(var, value) phar_write_16(var, (uint16_t) (value));
41
phar_zip_process_extra(php_stream * fp,phar_entry_info * entry,uint16_t len)42 static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, uint16_t len) /* {{{ */
43 {
44 union {
45 phar_zip_extra_field_header header;
46 phar_zip_unix3 unix3;
47 } h;
48 size_t read;
49
50 do {
51 if (sizeof(h.header) != php_stream_read(fp, (char *) &h.header, sizeof(h.header))) {
52 return FAILURE;
53 }
54
55 if (h.header.tag[0] != 'n' || h.header.tag[1] != 'u') {
56 /* skip to next header */
57 php_stream_seek(fp, PHAR_GET_16(h.header.size), SEEK_CUR);
58 len -= PHAR_GET_16(h.header.size) + 4;
59 continue;
60 }
61
62 /* unix3 header found */
63 read = php_stream_read(fp, (char *) &(h.unix3.crc32), sizeof(h.unix3) - sizeof(h.header));
64 len -= read + 4;
65
66 if (sizeof(h.unix3) - sizeof(h.header) != read) {
67 return FAILURE;
68 }
69
70 if (PHAR_GET_16(h.unix3.size) > sizeof(h.unix3) - 4) {
71 /* skip symlink filename - we may add this support in later */
72 php_stream_seek(fp, PHAR_GET_16(h.unix3.size) - sizeof(h.unix3.size), SEEK_CUR);
73 }
74
75 /* set permissions */
76 entry->flags &= PHAR_ENT_COMPRESSION_MASK;
77
78 if (entry->is_dir) {
79 entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK;
80 } else {
81 entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK;
82 }
83
84 } while (len);
85
86 return SUCCESS;
87 }
88 /* }}} */
89
90 /*
91 extracted from libzip
92 zip_dirent.c -- read directory entry (local or central), clean dirent
93 Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner
94
95 This function is part of libzip, a library to manipulate ZIP archives.
96 The authors can be contacted at <nih@giga.or.at>
97
98 Redistribution and use in source and binary forms, with or without
99 modification, are permitted provided that the following conditions
100 are met:
101 1. Redistributions of source code must retain the above copyright
102 notice, this list of conditions and the following disclaimer.
103 2. Redistributions in binary form must reproduce the above copyright
104 notice, this list of conditions and the following disclaimer in
105 the documentation and/or other materials provided with the
106 distribution.
107 3. The names of the authors may not be used to endorse or promote
108 products derived from this software without specific prior
109 written permission.
110
111 THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
112 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
113 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
114 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
115 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
116 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
117 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
118 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
119 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
120 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
121 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
122 */
phar_zip_d2u_time(char * cdtime,char * cddate)123 static time_t phar_zip_d2u_time(char *cdtime, char *cddate) /* {{{ */
124 {
125 int dtime = PHAR_GET_16(cdtime), ddate = PHAR_GET_16(cddate);
126 struct tm *tm, tmbuf;
127 time_t now;
128
129 now = time(NULL);
130 tm = php_localtime_r(&now, &tmbuf);
131
132 tm->tm_year = ((ddate>>9)&127) + 1980 - 1900;
133 tm->tm_mon = ((ddate>>5)&15) - 1;
134 tm->tm_mday = ddate&31;
135
136 tm->tm_hour = (dtime>>11)&31;
137 tm->tm_min = (dtime>>5)&63;
138 tm->tm_sec = (dtime<<1)&62;
139
140 return mktime(tm);
141 }
142 /* }}} */
143
phar_zip_u2d_time(time_t time,char * dtime,char * ddate)144 static void phar_zip_u2d_time(time_t time, char *dtime, char *ddate) /* {{{ */
145 {
146 uint16_t ctime, cdate;
147 struct tm *tm, tmbuf;
148
149 tm = php_localtime_r(&time, &tmbuf);
150 if (tm->tm_year >= 1980) {
151 cdate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + tm->tm_mday;
152 ctime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + ((tm->tm_sec)>>1);
153 } else {
154 /* This is the earliest date/time supported by zip. */
155 cdate = (1<<5) + 1; /* 1980-01-01 */
156 ctime = 0; /* 00:00:00 */
157 }
158
159 PHAR_SET_16(dtime, ctime);
160 PHAR_SET_16(ddate, cdate);
161 }
162 /* }}} */
163
phar_find_eocd(const char * s,size_t n)164 static char *phar_find_eocd(const char *s, size_t n)
165 {
166 const char *end = s + n + sizeof("PK\5\6") - 1 - sizeof(phar_zip_dir_end);
167
168 /* search backwards for end of central directory signatures */
169 do {
170 uint16_t comment_len;
171 const char *eocd_start = zend_memnrstr(s, "PK\5\6", sizeof("PK\5\6") - 1, end);
172
173 if (eocd_start == NULL) {
174 return NULL;
175 }
176 ZEND_ASSERT(eocd_start + sizeof(phar_zip_dir_end) <= s + n);
177 comment_len = PHAR_GET_16(((phar_zip_dir_end *) eocd_start)->comment_len);
178 if (eocd_start + sizeof(phar_zip_dir_end) + comment_len == s + n) {
179 /* we can't be sure, but this looks like the proper EOCD signature */
180 return (char *) eocd_start;
181 }
182 end = eocd_start;
183 } while (end > s);
184 return NULL;
185 }
186
187 /**
188 * Does not check for a previously opened phar in the cache.
189 *
190 * Parse a new one and add it to the cache, returning either SUCCESS or
191 * FAILURE, and setting pphar to the pointer to the manifest entry
192 *
193 * This is used by phar_open_from_fp to process a zip-based phar, but can be called
194 * directly.
195 */
phar_parse_zipfile(php_stream * fp,char * fname,size_t fname_len,char * alias,size_t alias_len,phar_archive_data ** pphar,char ** error)196 int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alias, size_t alias_len, phar_archive_data** pphar, char **error) /* {{{ */
197 {
198 phar_zip_dir_end locator;
199 char buf[sizeof(locator) + 65536];
200 zend_off_t size;
201 uint16_t i;
202 phar_archive_data *mydata = NULL;
203 phar_entry_info entry = {0};
204 char *p = buf, *ext, *actual_alias = NULL;
205 char *metadata = NULL;
206
207 size = php_stream_tell(fp);
208
209 if (size > sizeof(locator) + 65536) {
210 /* seek to max comment length + end of central directory record */
211 size = sizeof(locator) + 65536;
212 if (FAILURE == php_stream_seek(fp, -size, SEEK_END)) {
213 php_stream_close(fp);
214 if (error) {
215 spprintf(error, 4096, "phar error: unable to search for end of central directory in zip-based phar \"%s\"", fname);
216 }
217 return FAILURE;
218 }
219 } else {
220 php_stream_seek(fp, 0, SEEK_SET);
221 }
222
223 if (!php_stream_read(fp, buf, size)) {
224 php_stream_close(fp);
225 if (error) {
226 spprintf(error, 4096, "phar error: unable to read in data to search for end of central directory in zip-based phar \"%s\"", fname);
227 }
228 return FAILURE;
229 }
230
231 if ((p = phar_find_eocd(buf, size)) != NULL) {
232 memcpy((void *)&locator, (void *) p, sizeof(locator));
233 if (PHAR_GET_16(locator.centraldisk) != 0 || PHAR_GET_16(locator.disknumber) != 0) {
234 /* split archives not handled */
235 php_stream_close(fp);
236 if (error) {
237 spprintf(error, 4096, "phar error: split archives spanning multiple zips cannot be processed in zip-based phar \"%s\"", fname);
238 }
239 return FAILURE;
240 }
241
242 if (PHAR_GET_16(locator.counthere) != PHAR_GET_16(locator.count)) {
243 if (error) {
244 spprintf(error, 4096, "phar error: corrupt zip archive, conflicting file count in end of central directory record in zip-based phar \"%s\"", fname);
245 }
246 php_stream_close(fp);
247 return FAILURE;
248 }
249
250 mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
251 mydata->is_persistent = PHAR_G(persist);
252
253 /* read in archive comment, if any */
254 if (PHAR_GET_16(locator.comment_len)) {
255
256 metadata = p + sizeof(locator);
257
258 if (PHAR_GET_16(locator.comment_len) != size - (metadata - buf)) {
259 if (error) {
260 spprintf(error, 4096, "phar error: corrupt zip archive, zip file comment truncated in zip-based phar \"%s\"", fname);
261 }
262 php_stream_close(fp);
263 pefree(mydata, mydata->is_persistent);
264 return FAILURE;
265 }
266
267 mydata->metadata_len = PHAR_GET_16(locator.comment_len);
268
269 if (phar_parse_metadata(&metadata, &mydata->metadata, PHAR_GET_16(locator.comment_len)) == FAILURE) {
270 mydata->metadata_len = 0;
271 /* if not valid serialized data, it is a regular string */
272
273 ZVAL_NEW_STR(&mydata->metadata, zend_string_init(metadata, PHAR_GET_16(locator.comment_len), mydata->is_persistent));
274 }
275 } else {
276 ZVAL_UNDEF(&mydata->metadata);
277 }
278
279 goto foundit;
280 }
281
282 php_stream_close(fp);
283
284 if (error) {
285 spprintf(error, 4096, "phar error: end of central directory not found in zip-based phar \"%s\"", fname);
286 }
287
288 return FAILURE;
289 foundit:
290 mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
291 #ifdef PHP_WIN32
292 phar_unixify_path_separators(mydata->fname, fname_len);
293 #endif
294 mydata->is_zip = 1;
295 mydata->fname_len = fname_len;
296 ext = strrchr(mydata->fname, '/');
297
298 if (ext) {
299 mydata->ext = memchr(ext, '.', (mydata->fname + fname_len) - ext);
300 if (mydata->ext == ext) {
301 mydata->ext = memchr(ext + 1, '.', (mydata->fname + fname_len) - ext - 1);
302 }
303 if (mydata->ext) {
304 mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
305 }
306 }
307
308 /* clean up on big-endian systems */
309 /* seek to central directory */
310 php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
311 /* read in central directory */
312 zend_hash_init(&mydata->manifest, PHAR_GET_16(locator.count),
313 zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
314 zend_hash_init(&mydata->mounted_dirs, 5,
315 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
316 zend_hash_init(&mydata->virtual_dirs, PHAR_GET_16(locator.count) * 2,
317 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
318 entry.phar = mydata;
319 entry.is_zip = 1;
320 entry.fp_type = PHAR_FP;
321 entry.is_persistent = mydata->is_persistent;
322 #define PHAR_ZIP_FAIL_FREE(errmsg, save) \
323 zend_hash_destroy(&mydata->manifest); \
324 HT_INVALIDATE(&mydata->manifest); \
325 zend_hash_destroy(&mydata->mounted_dirs); \
326 HT_INVALIDATE(&mydata->mounted_dirs); \
327 zend_hash_destroy(&mydata->virtual_dirs); \
328 HT_INVALIDATE(&mydata->virtual_dirs); \
329 php_stream_close(fp); \
330 zval_ptr_dtor(&mydata->metadata); \
331 if (mydata->signature) { \
332 efree(mydata->signature); \
333 } \
334 if (error) { \
335 spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
336 } \
337 pefree(mydata->fname, mydata->is_persistent); \
338 if (mydata->alias) { \
339 pefree(mydata->alias, mydata->is_persistent); \
340 } \
341 pefree(mydata, mydata->is_persistent); \
342 efree(save); \
343 return FAILURE;
344 #define PHAR_ZIP_FAIL(errmsg) \
345 zend_hash_destroy(&mydata->manifest); \
346 HT_INVALIDATE(&mydata->manifest); \
347 zend_hash_destroy(&mydata->mounted_dirs); \
348 HT_INVALIDATE(&mydata->mounted_dirs); \
349 zend_hash_destroy(&mydata->virtual_dirs); \
350 HT_INVALIDATE(&mydata->virtual_dirs); \
351 php_stream_close(fp); \
352 zval_ptr_dtor(&mydata->metadata); \
353 if (mydata->signature) { \
354 efree(mydata->signature); \
355 } \
356 if (error) { \
357 spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
358 } \
359 pefree(mydata->fname, mydata->is_persistent); \
360 if (mydata->alias) { \
361 pefree(mydata->alias, mydata->is_persistent); \
362 } \
363 pefree(mydata, mydata->is_persistent); \
364 return FAILURE;
365
366 /* add each central directory item to the manifest */
367 for (i = 0; i < PHAR_GET_16(locator.count); ++i) {
368 phar_zip_central_dir_file zipentry;
369 zend_off_t beforeus = php_stream_tell(fp);
370
371 if (sizeof(zipentry) != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry))) {
372 PHAR_ZIP_FAIL("unable to read central directory entry, truncated");
373 }
374
375 /* clean up for bigendian systems */
376 if (memcmp("PK\1\2", zipentry.signature, 4)) {
377 /* corrupted entry */
378 PHAR_ZIP_FAIL("corrupted central directory entry, no magic signature");
379 }
380
381 if (entry.is_persistent) {
382 entry.manifest_pos = i;
383 }
384
385 entry.compressed_filesize = PHAR_GET_32(zipentry.compsize);
386 entry.uncompressed_filesize = PHAR_GET_32(zipentry.uncompsize);
387 entry.crc32 = PHAR_GET_32(zipentry.crc32);
388 /* do not PHAR_GET_16 either on the next line */
389 entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp);
390 entry.flags = PHAR_ENT_PERM_DEF_FILE;
391 entry.header_offset = PHAR_GET_32(zipentry.offset);
392 entry.offset = entry.offset_abs = PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + PHAR_GET_16(zipentry.filename_len) +
393 PHAR_GET_16(zipentry.extra_len);
394
395 if (PHAR_GET_16(zipentry.flags) & PHAR_ZIP_FLAG_ENCRYPTED) {
396 PHAR_ZIP_FAIL("Cannot process encrypted zip files");
397 }
398
399 if (!PHAR_GET_16(zipentry.filename_len)) {
400 PHAR_ZIP_FAIL("Cannot process zips created from stdin (zero-length filename)");
401 }
402
403 entry.filename_len = PHAR_GET_16(zipentry.filename_len);
404 entry.filename = (char *) pemalloc(entry.filename_len + 1, entry.is_persistent);
405
406 if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) {
407 pefree(entry.filename, entry.is_persistent);
408 PHAR_ZIP_FAIL("unable to read in filename from central directory, truncated");
409 }
410
411 entry.filename[entry.filename_len] = '\0';
412
413 if (entry.filename[entry.filename_len - 1] == '/') {
414 entry.is_dir = 1;
415 if(entry.filename_len > 1) {
416 entry.filename_len--;
417 }
418 entry.flags |= PHAR_ENT_PERM_DEF_DIR;
419 } else {
420 entry.is_dir = 0;
421 }
422
423 if (entry.filename_len == sizeof(".phar/signature.bin")-1 && !strncmp(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
424 size_t read;
425 php_stream *sigfile;
426 char *sig;
427 size_t sig_len;
428
429 pefree(entry.filename, entry.is_persistent);
430
431 if (entry.uncompressed_filesize > 0x10000) {
432 PHAR_ZIP_FAIL("signatures larger than 64 KiB are not supported");
433 }
434
435 php_stream_tell(fp);
436 sigfile = php_stream_fopen_tmpfile();
437 if (!sigfile) {
438 PHAR_ZIP_FAIL("couldn't open temporary file");
439 }
440
441 php_stream_seek(fp, 0, SEEK_SET);
442 /* copy file contents + local headers and zip comment, if any, to be hashed for signature */
443 php_stream_copy_to_stream_ex(fp, sigfile, entry.header_offset, NULL);
444 /* seek to central directory */
445 php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
446 /* copy central directory header */
447 php_stream_copy_to_stream_ex(fp, sigfile, beforeus - PHAR_GET_32(locator.cdir_offset), NULL);
448 if (metadata) {
449 php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len));
450 }
451 php_stream_seek(fp, sizeof(phar_zip_file_header) + entry.header_offset + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
452 sig = (char *) emalloc(entry.uncompressed_filesize);
453 read = php_stream_read(fp, sig, entry.uncompressed_filesize);
454 if (read != entry.uncompressed_filesize || read <= 8) {
455 php_stream_close(sigfile);
456 efree(sig);
457 PHAR_ZIP_FAIL("signature cannot be read");
458 }
459 mydata->sig_flags = PHAR_GET_32(sig);
460 if (FAILURE == phar_verify_signature(sigfile, php_stream_tell(sigfile), mydata->sig_flags, sig + 8, entry.uncompressed_filesize - 8, fname, &mydata->signature, &sig_len, error)) {
461 efree(sig);
462 if (error) {
463 char *save;
464 php_stream_close(sigfile);
465 spprintf(&save, 4096, "signature cannot be verified: %s", *error);
466 efree(*error);
467 PHAR_ZIP_FAIL_FREE(save, save);
468 } else {
469 php_stream_close(sigfile);
470 PHAR_ZIP_FAIL("signature cannot be verified");
471 }
472 }
473 mydata->sig_len = sig_len;
474 php_stream_close(sigfile);
475 efree(sig);
476 /* signature checked out, let's ensure this is the last file in the phar */
477 if (i != PHAR_GET_16(locator.count) - 1) {
478 PHAR_ZIP_FAIL("entries exist after signature, invalid phar");
479 }
480
481 continue;
482 }
483
484 phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len);
485
486 if (PHAR_GET_16(zipentry.extra_len)) {
487 zend_off_t loc = php_stream_tell(fp);
488 if (FAILURE == phar_zip_process_extra(fp, &entry, PHAR_GET_16(zipentry.extra_len))) {
489 pefree(entry.filename, entry.is_persistent);
490 PHAR_ZIP_FAIL("Unable to process extra field header for file in central directory");
491 }
492 php_stream_seek(fp, loc + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
493 }
494
495 switch (PHAR_GET_16(zipentry.compressed)) {
496 case PHAR_ZIP_COMP_NONE :
497 /* compression flag already set */
498 break;
499 case PHAR_ZIP_COMP_DEFLATE :
500 entry.flags |= PHAR_ENT_COMPRESSED_GZ;
501 if (!PHAR_G(has_zlib)) {
502 pefree(entry.filename, entry.is_persistent);
503 PHAR_ZIP_FAIL("zlib extension is required");
504 }
505 break;
506 case PHAR_ZIP_COMP_BZIP2 :
507 entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
508 if (!PHAR_G(has_bz2)) {
509 pefree(entry.filename, entry.is_persistent);
510 PHAR_ZIP_FAIL("bzip2 extension is required");
511 }
512 break;
513 case 1 :
514 pefree(entry.filename, entry.is_persistent);
515 PHAR_ZIP_FAIL("unsupported compression method (Shrunk) used in this zip");
516 case 2 :
517 case 3 :
518 case 4 :
519 case 5 :
520 pefree(entry.filename, entry.is_persistent);
521 PHAR_ZIP_FAIL("unsupported compression method (Reduce) used in this zip");
522 case 6 :
523 pefree(entry.filename, entry.is_persistent);
524 PHAR_ZIP_FAIL("unsupported compression method (Implode) used in this zip");
525 case 7 :
526 pefree(entry.filename, entry.is_persistent);
527 PHAR_ZIP_FAIL("unsupported compression method (Tokenize) used in this zip");
528 case 9 :
529 pefree(entry.filename, entry.is_persistent);
530 PHAR_ZIP_FAIL("unsupported compression method (Deflate64) used in this zip");
531 case 10 :
532 pefree(entry.filename, entry.is_persistent);
533 PHAR_ZIP_FAIL("unsupported compression method (PKWare Implode/old IBM TERSE) used in this zip");
534 case 14 :
535 pefree(entry.filename, entry.is_persistent);
536 PHAR_ZIP_FAIL("unsupported compression method (LZMA) used in this zip");
537 case 18 :
538 pefree(entry.filename, entry.is_persistent);
539 PHAR_ZIP_FAIL("unsupported compression method (IBM TERSE) used in this zip");
540 case 19 :
541 pefree(entry.filename, entry.is_persistent);
542 PHAR_ZIP_FAIL("unsupported compression method (IBM LZ77) used in this zip");
543 case 97 :
544 pefree(entry.filename, entry.is_persistent);
545 PHAR_ZIP_FAIL("unsupported compression method (WavPack) used in this zip");
546 case 98 :
547 pefree(entry.filename, entry.is_persistent);
548 PHAR_ZIP_FAIL("unsupported compression method (PPMd) used in this zip");
549 default :
550 pefree(entry.filename, entry.is_persistent);
551 PHAR_ZIP_FAIL("unsupported compression method (unknown) used in this zip");
552 }
553
554 /* get file metadata */
555 if (PHAR_GET_16(zipentry.comment_len)) {
556 if (PHAR_GET_16(zipentry.comment_len) != php_stream_read(fp, buf, PHAR_GET_16(zipentry.comment_len))) {
557 pefree(entry.filename, entry.is_persistent);
558 PHAR_ZIP_FAIL("unable to read in file comment, truncated");
559 }
560
561 p = buf;
562 entry.metadata_len = PHAR_GET_16(zipentry.comment_len);
563
564 if (phar_parse_metadata(&p, &(entry.metadata), PHAR_GET_16(zipentry.comment_len)) == FAILURE) {
565 entry.metadata_len = 0;
566 /* if not valid serialized data, it is a regular string */
567
568 ZVAL_NEW_STR(&entry.metadata, zend_string_init(buf, PHAR_GET_16(zipentry.comment_len), entry.is_persistent));
569 }
570 } else {
571 ZVAL_UNDEF(&entry.metadata);
572 }
573
574 if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
575 php_stream_filter *filter;
576 zend_off_t saveloc;
577 /* verify local file header */
578 phar_zip_file_header local;
579
580 /* archive alias found */
581 saveloc = php_stream_tell(fp);
582 php_stream_seek(fp, PHAR_GET_32(zipentry.offset), SEEK_SET);
583
584 if (sizeof(local) != php_stream_read(fp, (char *) &local, sizeof(local))) {
585 pefree(entry.filename, entry.is_persistent);
586 PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (cannot read local file header for alias)");
587 }
588
589 /* verify local header */
590 if (entry.filename_len != PHAR_GET_16(local.filename_len) || entry.crc32 != PHAR_GET_32(local.crc32) || entry.uncompressed_filesize != PHAR_GET_32(local.uncompsize) || entry.compressed_filesize != PHAR_GET_32(local.compsize)) {
591 pefree(entry.filename, entry.is_persistent);
592 PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (local header of alias does not match central directory)");
593 }
594
595 /* construct actual offset to file start - local extra_len can be different from central extra_len */
596 entry.offset = entry.offset_abs =
597 sizeof(local) + entry.header_offset + PHAR_GET_16(local.filename_len) + PHAR_GET_16(local.extra_len);
598 php_stream_seek(fp, entry.offset, SEEK_SET);
599 /* these next lines should be for php < 5.2.6 after 5.3 filters are fixed */
600 fp->writepos = 0;
601 fp->readpos = 0;
602 php_stream_seek(fp, entry.offset, SEEK_SET);
603 fp->writepos = 0;
604 fp->readpos = 0;
605 /* the above lines should be for php < 5.2.6 after 5.3 filters are fixed */
606
607 mydata->alias_len = entry.uncompressed_filesize;
608 if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
609 filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp));
610
611 if (!filter) {
612 pefree(entry.filename, entry.is_persistent);
613 PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed");
614 }
615
616 php_stream_filter_append(&fp->readfilters, filter);
617
618 // TODO: refactor to avoid reallocation ???
619 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
620 {
621 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
622 if (str) {
623 entry.uncompressed_filesize = ZSTR_LEN(str);
624 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
625 zend_string_release_ex(str, 0);
626 } else {
627 actual_alias = NULL;
628 entry.uncompressed_filesize = 0;
629 }
630 }
631
632 if (!entry.uncompressed_filesize || !actual_alias) {
633 pefree(entry.filename, entry.is_persistent);
634 PHAR_ZIP_FAIL("unable to read in alias, truncated");
635 }
636
637 php_stream_filter_flush(filter, 1);
638 php_stream_filter_remove(filter, 1);
639
640 } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
641 filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp));
642
643 if (!filter) {
644 pefree(entry.filename, entry.is_persistent);
645 PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed");
646 }
647
648 php_stream_filter_append(&fp->readfilters, filter);
649
650 // TODO: refactor to avoid reallocation ???
651 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
652 {
653 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
654 if (str) {
655 entry.uncompressed_filesize = ZSTR_LEN(str);
656 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
657 zend_string_release_ex(str, 0);
658 } else {
659 actual_alias = NULL;
660 entry.uncompressed_filesize = 0;
661 }
662 }
663
664 if (!entry.uncompressed_filesize || !actual_alias) {
665 pefree(entry.filename, entry.is_persistent);
666 PHAR_ZIP_FAIL("unable to read in alias, truncated");
667 }
668
669 php_stream_filter_flush(filter, 1);
670 php_stream_filter_remove(filter, 1);
671 } else {
672 // TODO: refactor to avoid reallocation ???
673 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
674 {
675 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
676 if (str) {
677 entry.uncompressed_filesize = ZSTR_LEN(str);
678 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
679 zend_string_release_ex(str, 0);
680 } else {
681 actual_alias = NULL;
682 entry.uncompressed_filesize = 0;
683 }
684 }
685
686 if (!entry.uncompressed_filesize || !actual_alias) {
687 pefree(entry.filename, entry.is_persistent);
688 PHAR_ZIP_FAIL("unable to read in alias, truncated");
689 }
690 }
691
692 /* return to central directory parsing */
693 php_stream_seek(fp, saveloc, SEEK_SET);
694 }
695
696 phar_set_inode(&entry);
697 zend_hash_str_add_mem(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry, sizeof(phar_entry_info));
698 }
699
700 mydata->fp = fp;
701
702 if (zend_hash_str_exists(&(mydata->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
703 mydata->is_data = 0;
704 } else {
705 mydata->is_data = 1;
706 }
707
708 zend_hash_str_add_ptr(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len, mydata);
709
710 if (actual_alias) {
711 phar_archive_data *fd_ptr;
712
713 if (!phar_validate_alias(actual_alias, mydata->alias_len)) {
714 if (error) {
715 spprintf(error, 4096, "phar error: invalid alias \"%s\" in zip-based phar \"%s\"", actual_alias, fname);
716 }
717 efree(actual_alias);
718 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
719 return FAILURE;
720 }
721
722 mydata->is_temporary_alias = 0;
723
724 if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), actual_alias, mydata->alias_len))) {
725 if (SUCCESS != phar_free_alias(fd_ptr, actual_alias, mydata->alias_len)) {
726 if (error) {
727 spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with implicit alias, alias is already in use", fname);
728 }
729 efree(actual_alias);
730 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
731 return FAILURE;
732 }
733 }
734
735 mydata->alias = entry.is_persistent ? pestrndup(actual_alias, mydata->alias_len, 1) : actual_alias;
736
737 if (entry.is_persistent) {
738 efree(actual_alias);
739 }
740
741 zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), mydata->alias, mydata->alias_len, mydata);
742 } else {
743 phar_archive_data *fd_ptr;
744
745 if (alias_len) {
746 if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
747 if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
748 if (error) {
749 spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with explicit alias, alias is already in use", fname);
750 }
751 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
752 return FAILURE;
753 }
754 }
755
756 zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), actual_alias, mydata->alias_len, mydata);
757 mydata->alias = pestrndup(alias, alias_len, mydata->is_persistent);
758 mydata->alias_len = alias_len;
759 } else {
760 mydata->alias = pestrndup(mydata->fname, fname_len, mydata->is_persistent);
761 mydata->alias_len = fname_len;
762 }
763
764 mydata->is_temporary_alias = 1;
765 }
766
767 if (pphar) {
768 *pphar = mydata;
769 }
770
771 return SUCCESS;
772 }
773 /* }}} */
774
775 /**
776 * Create or open a zip-based phar for writing
777 */
phar_open_or_create_zip(char * fname,size_t fname_len,char * alias,size_t alias_len,int is_data,uint32_t options,phar_archive_data ** pphar,char ** error)778 int phar_open_or_create_zip(char *fname, size_t fname_len, char *alias, size_t alias_len, int is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */
779 {
780 phar_archive_data *phar;
781 int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error);
782
783 if (FAILURE == ret) {
784 return FAILURE;
785 }
786
787 if (pphar) {
788 *pphar = phar;
789 }
790
791 phar->is_data = is_data;
792
793 if (phar->is_zip) {
794 return ret;
795 }
796
797 if (phar->is_brandnew) {
798 phar->internal_file_start = 0;
799 phar->is_zip = 1;
800 phar->is_tar = 0;
801 return SUCCESS;
802 }
803
804 /* we've reached here - the phar exists and is a regular phar */
805 if (error) {
806 spprintf(error, 4096, "phar zip error: phar \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a zip-based phar", fname);
807 }
808
809 return FAILURE;
810 }
811 /* }}} */
812
813 struct _phar_zip_pass {
814 php_stream *filefp;
815 php_stream *centralfp;
816 php_stream *old;
817 int free_fp;
818 int free_ufp;
819 char **error;
820 };
821 /* perform final modification of zip contents for each file in the manifest before saving */
phar_zip_changed_apply_int(phar_entry_info * entry,void * arg)822 static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ */
823 {
824 phar_zip_file_header local;
825 phar_zip_unix3 perms;
826 phar_zip_central_dir_file central;
827 struct _phar_zip_pass *p;
828 uint32_t newcrc32;
829 zend_off_t offset;
830 int not_really_modified = 0;
831 p = (struct _phar_zip_pass*) arg;
832 uint16_t general_purpose_flags;
833
834 if (entry->is_mounted) {
835 return ZEND_HASH_APPLY_KEEP;
836 }
837
838 if (entry->is_deleted) {
839 if (entry->fp_refcount <= 0) {
840 return ZEND_HASH_APPLY_REMOVE;
841 } else {
842 /* we can't delete this in-memory until it is closed */
843 return ZEND_HASH_APPLY_KEEP;
844 }
845 }
846
847 phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len);
848 memset(&local, 0, sizeof(local));
849 memset(¢ral, 0, sizeof(central));
850 memset(&perms, 0, sizeof(perms));
851 memcpy(local.signature, "PK\3\4", 4);
852 memcpy(central.signature, "PK\1\2", 4);
853 PHAR_SET_16(central.extra_len, sizeof(perms));
854 PHAR_SET_16(local.extra_len, sizeof(perms));
855 perms.tag[0] = 'n';
856 perms.tag[1] = 'u';
857 PHAR_SET_16(perms.size, sizeof(perms) - 4);
858 PHAR_SET_16(perms.perms, entry->flags & PHAR_ENT_PERM_MASK);
859 {
860 uint32_t crc = (uint32_t) ~0;
861 CRC32(crc, perms.perms[0]);
862 CRC32(crc, perms.perms[1]);
863 PHAR_SET_32(perms.crc32, ~crc);
864 }
865
866 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
867 PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_DEFLATE);
868 PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_DEFLATE);
869 }
870
871 if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
872 PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_BZIP2);
873 PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_BZIP2);
874 }
875
876 /* do not use PHAR_GET_16 on either field of the next line */
877 phar_zip_u2d_time(entry->timestamp, local.timestamp, local.datestamp);
878 memcpy(central.timestamp, local.timestamp, sizeof(local.timestamp));
879 memcpy(central.datestamp, local.datestamp, sizeof(local.datestamp));
880 PHAR_SET_16(central.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
881 PHAR_SET_16(local.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
882 // set language encoding flag (all filenames have to be UTF-8 anyway)
883 general_purpose_flags = PHAR_GET_16(central.flags);
884 PHAR_SET_16(central.flags, general_purpose_flags | (1 << 11));
885 general_purpose_flags = PHAR_GET_16(local.flags);
886 PHAR_SET_16(local.flags, general_purpose_flags | (1 << 11));
887 PHAR_SET_32(central.offset, php_stream_tell(p->filefp));
888
889 /* do extra field for perms later */
890 if (entry->is_modified) {
891 uint32_t loc;
892 php_stream_filter *filter;
893 php_stream *efp;
894
895 if (entry->is_dir) {
896 entry->is_modified = 0;
897 if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
898 php_stream_close(entry->fp);
899 entry->fp = NULL;
900 entry->fp_type = PHAR_FP;
901 }
902 goto continue_dir;
903 }
904
905 if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
906 spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
907 return ZEND_HASH_APPLY_STOP;
908 }
909
910 /* we can be modified and already be compressed, such as when chmod() is executed */
911 if (entry->flags & PHAR_ENT_COMPRESSION_MASK && (entry->old_flags == entry->flags || !entry->old_flags)) {
912 not_really_modified = 1;
913 goto is_compressed;
914 }
915
916 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
917 spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
918 return ZEND_HASH_APPLY_STOP;
919 }
920
921 efp = phar_get_efp(entry, 0);
922 newcrc32 = ~0;
923
924 for (loc = 0;loc < entry->uncompressed_filesize; ++loc) {
925 CRC32(newcrc32, php_stream_getc(efp));
926 }
927
928 entry->crc32 = ~newcrc32;
929 PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
930 PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
931
932 if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
933 /* not compressed */
934 entry->compressed_filesize = entry->uncompressed_filesize;
935 PHAR_SET_32(central.compsize, entry->uncompressed_filesize);
936 PHAR_SET_32(local.compsize, entry->uncompressed_filesize);
937 goto not_compressed;
938 }
939
940 filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
941
942 if (!filter) {
943 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
944 spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
945 } else {
946 spprintf(p->error, 0, "unable to bzip2 compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
947 }
948 return ZEND_HASH_APPLY_STOP;
949 }
950
951 /* create new file that holds the compressed version */
952 /* work around inability to specify freedom in write and strictness
953 in read count */
954 entry->cfp = php_stream_fopen_tmpfile();
955
956 if (!entry->cfp) {
957 spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
958 return ZEND_HASH_APPLY_STOP;
959 }
960
961 php_stream_flush(efp);
962
963 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
964 spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
965 return ZEND_HASH_APPLY_STOP;
966 }
967
968 php_stream_filter_append((&entry->cfp->writefilters), filter);
969
970 if (SUCCESS != php_stream_copy_to_stream_ex(efp, entry->cfp, entry->uncompressed_filesize, NULL)) {
971 spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname);
972 return ZEND_HASH_APPLY_STOP;
973 }
974
975 php_stream_filter_flush(filter, 1);
976 php_stream_flush(entry->cfp);
977 php_stream_filter_remove(filter, 1);
978 php_stream_seek(entry->cfp, 0, SEEK_END);
979 entry->compressed_filesize = (uint32_t) php_stream_tell(entry->cfp);
980 PHAR_SET_32(central.compsize, entry->compressed_filesize);
981 PHAR_SET_32(local.compsize, entry->compressed_filesize);
982 /* generate crc on compressed file */
983 php_stream_rewind(entry->cfp);
984 entry->old_flags = entry->flags;
985 entry->is_modified = 1;
986 } else {
987 is_compressed:
988 PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
989 PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
990 PHAR_SET_32(central.compsize, entry->compressed_filesize);
991 PHAR_SET_32(local.compsize, entry->compressed_filesize);
992 if (p->old) {
993 if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) {
994 spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
995 return ZEND_HASH_APPLY_STOP;
996 }
997 }
998 }
999 not_compressed:
1000 PHAR_SET_32(central.crc32, entry->crc32);
1001 PHAR_SET_32(local.crc32, entry->crc32);
1002 continue_dir:
1003 /* set file metadata */
1004 if (Z_TYPE(entry->metadata) != IS_UNDEF) {
1005 php_serialize_data_t metadata_hash;
1006
1007 if (entry->metadata_str.s) {
1008 smart_str_free(&entry->metadata_str);
1009 }
1010 entry->metadata_str.s = NULL;
1011 PHP_VAR_SERIALIZE_INIT(metadata_hash);
1012 php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash);
1013 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
1014 PHAR_SET_16(central.comment_len, ZSTR_LEN(entry->metadata_str.s));
1015 }
1016
1017 entry->header_offset = php_stream_tell(p->filefp);
1018 offset = entry->header_offset + sizeof(local) + entry->filename_len + (entry->is_dir ? 1 : 0) + sizeof(perms);
1019
1020 if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) {
1021 spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1022 return ZEND_HASH_APPLY_STOP;
1023 }
1024
1025 if (sizeof(central) != php_stream_write(p->centralfp, (char *)¢ral, sizeof(central))) {
1026 spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1027 return ZEND_HASH_APPLY_STOP;
1028 }
1029
1030 if (entry->is_dir) {
1031 if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
1032 spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1033 return ZEND_HASH_APPLY_STOP;
1034 }
1035
1036 if (1 != php_stream_write(p->filefp, "/", 1)) {
1037 spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1038 return ZEND_HASH_APPLY_STOP;
1039 }
1040
1041 if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1042 spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1043 return ZEND_HASH_APPLY_STOP;
1044 }
1045
1046 if (1 != php_stream_write(p->centralfp, "/", 1)) {
1047 spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1048 return ZEND_HASH_APPLY_STOP;
1049 }
1050 } else {
1051 if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
1052 spprintf(p->error, 0, "unable to write filename to local directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1053 return ZEND_HASH_APPLY_STOP;
1054 }
1055
1056 if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1057 spprintf(p->error, 0, "unable to write filename to central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1058 return ZEND_HASH_APPLY_STOP;
1059 }
1060 }
1061
1062 if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) {
1063 spprintf(p->error, 0, "unable to write local extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1064 return ZEND_HASH_APPLY_STOP;
1065 }
1066
1067 if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) {
1068 spprintf(p->error, 0, "unable to write central extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1069 return ZEND_HASH_APPLY_STOP;
1070 }
1071
1072 if (!not_really_modified && entry->is_modified) {
1073 if (entry->cfp) {
1074 if (SUCCESS != php_stream_copy_to_stream_ex(entry->cfp, p->filefp, entry->compressed_filesize, NULL)) {
1075 spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1076 return ZEND_HASH_APPLY_STOP;
1077 }
1078
1079 php_stream_close(entry->cfp);
1080 entry->cfp = NULL;
1081 } else {
1082 if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
1083 return ZEND_HASH_APPLY_STOP;
1084 }
1085
1086 phar_seek_efp(entry, 0, SEEK_SET, 0, 0);
1087
1088 if (SUCCESS != php_stream_copy_to_stream_ex(phar_get_efp(entry, 0), p->filefp, entry->uncompressed_filesize, NULL)) {
1089 spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1090 return ZEND_HASH_APPLY_STOP;
1091 }
1092 }
1093
1094 if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) {
1095 php_stream_close(entry->fp);
1096 }
1097
1098 entry->is_modified = 0;
1099 } else {
1100 entry->is_modified = 0;
1101 if (entry->fp_refcount) {
1102 /* open file pointers refer to this fp, do not free the stream */
1103 switch (entry->fp_type) {
1104 case PHAR_FP:
1105 p->free_fp = 0;
1106 break;
1107 case PHAR_UFP:
1108 p->free_ufp = 0;
1109 default:
1110 break;
1111 }
1112 }
1113
1114 if (!entry->is_dir && entry->compressed_filesize && SUCCESS != php_stream_copy_to_stream_ex(p->old, p->filefp, entry->compressed_filesize, NULL)) {
1115 spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1116 return ZEND_HASH_APPLY_STOP;
1117 }
1118 }
1119
1120 entry->fp = NULL;
1121 entry->offset = entry->offset_abs = offset;
1122 entry->fp_type = PHAR_FP;
1123
1124 if (entry->metadata_str.s) {
1125 if (ZSTR_LEN(entry->metadata_str.s) != php_stream_write(p->centralfp, ZSTR_VAL(entry->metadata_str.s), ZSTR_LEN(entry->metadata_str.s))) {
1126 spprintf(p->error, 0, "unable to write metadata as file comment for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1127 smart_str_free(&entry->metadata_str);
1128 return ZEND_HASH_APPLY_STOP;
1129 }
1130
1131 smart_str_free(&entry->metadata_str);
1132 }
1133
1134 return ZEND_HASH_APPLY_KEEP;
1135 }
1136 /* }}} */
1137
phar_zip_changed_apply(zval * zv,void * arg)1138 static int phar_zip_changed_apply(zval *zv, void *arg) /* {{{ */
1139 {
1140 return phar_zip_changed_apply_int(Z_PTR_P(zv), arg);
1141 }
1142 /* }}} */
1143
phar_zip_applysignature(phar_archive_data * phar,struct _phar_zip_pass * pass,smart_str * metadata)1144 static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pass *pass,
1145 smart_str *metadata) /* {{{ */
1146 {
1147 /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
1148 if (!phar->is_data || phar->sig_flags) {
1149 size_t signature_length;
1150 char *signature, sigbuf[8];
1151 phar_entry_info entry = {0};
1152 php_stream *newfile;
1153 zend_off_t tell;
1154
1155 newfile = php_stream_fopen_tmpfile();
1156 if (newfile == NULL) {
1157 spprintf(pass->error, 0, "phar error: unable to create temporary file for the signature file");
1158 return FAILURE;
1159 }
1160 tell = php_stream_tell(pass->filefp);
1161 /* copy the local files, central directory, and the zip comment to generate the hash */
1162 php_stream_seek(pass->filefp, 0, SEEK_SET);
1163 php_stream_copy_to_stream_ex(pass->filefp, newfile, tell, NULL);
1164 tell = php_stream_tell(pass->centralfp);
1165 php_stream_seek(pass->centralfp, 0, SEEK_SET);
1166 php_stream_copy_to_stream_ex(pass->centralfp, newfile, tell, NULL);
1167 if (metadata->s) {
1168 php_stream_write(newfile, ZSTR_VAL(metadata->s), ZSTR_LEN(metadata->s));
1169 }
1170
1171 if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, pass->error)) {
1172 if (pass->error) {
1173 char *save = *(pass->error);
1174 spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", save);
1175 efree(save);
1176 }
1177
1178 php_stream_close(newfile);
1179 return FAILURE;
1180 }
1181
1182 entry.filename = ".phar/signature.bin";
1183 entry.filename_len = sizeof(".phar/signature.bin")-1;
1184 entry.fp = php_stream_fopen_tmpfile();
1185 entry.fp_type = PHAR_MOD;
1186 entry.is_modified = 1;
1187 if (entry.fp == NULL) {
1188 spprintf(pass->error, 0, "phar error: unable to create temporary file for signature");
1189 return FAILURE;
1190 }
1191
1192 PHAR_SET_32(sigbuf, phar->sig_flags);
1193 PHAR_SET_32(sigbuf + 4, signature_length);
1194
1195 if (Z_UL(8) != php_stream_write(entry.fp, sigbuf, 8) || signature_length != php_stream_write(entry.fp, signature, signature_length)) {
1196 efree(signature);
1197 if (pass->error) {
1198 spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar %s", phar->fname);
1199 }
1200
1201 php_stream_close(newfile);
1202 return FAILURE;
1203 }
1204
1205 efree(signature);
1206 entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
1207 entry.phar = phar;
1208 /* throw out return value and write the signature */
1209 phar_zip_changed_apply_int(&entry, (void *)pass);
1210 php_stream_close(newfile);
1211
1212 if (pass->error && *(pass->error)) {
1213 /* error is set by writeheaders */
1214 return FAILURE;
1215 }
1216 } /* signature */
1217 return SUCCESS;
1218 }
1219 /* }}} */
1220
phar_zip_flush(phar_archive_data * phar,char * user_stub,zend_long len,int defaultstub,char ** error)1221 int phar_zip_flush(phar_archive_data *phar, char *user_stub, zend_long len, int defaultstub, char **error) /* {{{ */
1222 {
1223 char *pos;
1224 smart_str main_metadata_str = {0};
1225 static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
1226 char halt_stub[] = "__HALT_COMPILER();";
1227 char *tmp;
1228
1229 php_stream *stubfile, *oldfile;
1230 php_serialize_data_t metadata_hash;
1231 int free_user_stub, closeoldfile = 0;
1232 phar_entry_info entry = {0};
1233 char *temperr = NULL;
1234 struct _phar_zip_pass pass;
1235 phar_zip_dir_end eocd;
1236 uint32_t cdir_size, cdir_offset;
1237
1238 pass.error = &temperr;
1239 entry.flags = PHAR_ENT_PERM_DEF_FILE;
1240 entry.timestamp = time(NULL);
1241 entry.is_modified = 1;
1242 entry.is_zip = 1;
1243 entry.phar = phar;
1244 entry.fp_type = PHAR_MOD;
1245
1246 if (phar->is_persistent) {
1247 if (error) {
1248 spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
1249 }
1250 return EOF;
1251 }
1252
1253 if (phar->is_data) {
1254 goto nostub;
1255 }
1256
1257 /* set alias */
1258 if (!phar->is_temporary_alias && phar->alias_len) {
1259 entry.fp = php_stream_fopen_tmpfile();
1260 if (entry.fp == NULL) {
1261 spprintf(error, 0, "phar error: unable to create temporary file");
1262 return EOF;
1263 }
1264 if (phar->alias_len != php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
1265 if (error) {
1266 spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1267 }
1268 return EOF;
1269 }
1270
1271 entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len;
1272 entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1273 entry.filename_len = sizeof(".phar/alias.txt")-1;
1274
1275 zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1276 } else {
1277 zend_hash_str_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1278 }
1279
1280 /* register alias */
1281 if (phar->alias_len) {
1282 if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error)) {
1283 return EOF;
1284 }
1285 }
1286
1287 /* set stub */
1288 if (user_stub && !defaultstub) {
1289 if (len < 0) {
1290 /* resource passed in */
1291 if (!(php_stream_from_zval_no_verify(stubfile, (zval *)user_stub))) {
1292 if (error) {
1293 spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1294 }
1295 return EOF;
1296 }
1297
1298 if (len == -1) {
1299 len = PHP_STREAM_COPY_ALL;
1300 } else {
1301 len = -len;
1302 }
1303
1304 user_stub = 0;
1305
1306 // TODO: refactor to avoid reallocation ???
1307 //??? len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)
1308 {
1309 zend_string *str = php_stream_copy_to_mem(stubfile, len, 0);
1310 if (str) {
1311 len = ZSTR_LEN(str);
1312 user_stub = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
1313 zend_string_release_ex(str, 0);
1314 } else {
1315 user_stub = NULL;
1316 len = 0;
1317 }
1318 }
1319
1320 if (!len || !user_stub) {
1321 if (error) {
1322 spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1323 }
1324 return EOF;
1325 }
1326 free_user_stub = 1;
1327 } else {
1328 free_user_stub = 0;
1329 }
1330
1331 tmp = estrndup(user_stub, len);
1332 if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
1333 efree(tmp);
1334 if (error) {
1335 spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
1336 }
1337 if (free_user_stub) {
1338 efree(user_stub);
1339 }
1340 return EOF;
1341 }
1342 pos = user_stub + (pos - tmp);
1343 efree(tmp);
1344
1345 len = pos - user_stub + 18;
1346 entry.fp = php_stream_fopen_tmpfile();
1347 if (entry.fp == NULL) {
1348 spprintf(error, 0, "phar error: unable to create temporary file");
1349 return EOF;
1350 }
1351 entry.uncompressed_filesize = len + 5;
1352
1353 if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
1354 || 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
1355 if (error) {
1356 spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
1357 }
1358 if (free_user_stub) {
1359 efree(user_stub);
1360 }
1361 php_stream_close(entry.fp);
1362 return EOF;
1363 }
1364
1365 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1366 entry.filename_len = sizeof(".phar/stub.php")-1;
1367
1368 zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1369
1370 if (free_user_stub) {
1371 efree(user_stub);
1372 }
1373 } else {
1374 /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1375 entry.fp = php_stream_fopen_tmpfile();
1376 if (entry.fp == NULL) {
1377 spprintf(error, 0, "phar error: unable to create temporary file");
1378 return EOF;
1379 }
1380 if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1381 php_stream_close(entry.fp);
1382 if (error) {
1383 spprintf(error, 0, "unable to %s stub in%szip-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1384 }
1385 return EOF;
1386 }
1387
1388 entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1389 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1390 entry.filename_len = sizeof(".phar/stub.php")-1;
1391
1392 if (!defaultstub) {
1393 if (!zend_hash_str_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1394 if (NULL == zend_hash_str_add_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1395 php_stream_close(entry.fp);
1396 efree(entry.filename);
1397 if (error) {
1398 spprintf(error, 0, "unable to create stub in zip-based phar \"%s\"", phar->fname);
1399 }
1400 return EOF;
1401 }
1402 } else {
1403 php_stream_close(entry.fp);
1404 efree(entry.filename);
1405 }
1406 } else {
1407 zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1408 }
1409 }
1410 nostub:
1411 if (phar->fp && !phar->is_brandnew) {
1412 oldfile = phar->fp;
1413 closeoldfile = 0;
1414 php_stream_rewind(oldfile);
1415 } else {
1416 oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1417 closeoldfile = oldfile != NULL;
1418 }
1419
1420 /* save modified files to the zip */
1421 pass.old = oldfile;
1422 pass.filefp = php_stream_fopen_tmpfile();
1423
1424 if (!pass.filefp) {
1425 fperror:
1426 if (closeoldfile) {
1427 php_stream_close(oldfile);
1428 }
1429 if (error) {
1430 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
1431 }
1432 return EOF;
1433 }
1434
1435 pass.centralfp = php_stream_fopen_tmpfile();
1436
1437 if (!pass.centralfp) {
1438 goto fperror;
1439 }
1440
1441 pass.free_fp = pass.free_ufp = 1;
1442 memset(&eocd, 0, sizeof(eocd));
1443
1444 memcpy(eocd.signature, "PK\5\6", 4);
1445 if (!phar->is_data && !phar->sig_flags) {
1446 phar->sig_flags = PHAR_SIG_SHA1;
1447 }
1448 if (phar->sig_flags) {
1449 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest) + 1);
1450 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest) + 1);
1451 } else {
1452 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
1453 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
1454 }
1455 zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass);
1456
1457 if (Z_TYPE(phar->metadata) != IS_UNDEF) {
1458 /* set phar metadata */
1459 PHP_VAR_SERIALIZE_INIT(metadata_hash);
1460 php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash);
1461 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
1462 }
1463 if (temperr) {
1464 if (error) {
1465 spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
1466 }
1467 efree(temperr);
1468 temperror:
1469 php_stream_close(pass.centralfp);
1470 nocentralerror:
1471 if (Z_TYPE(phar->metadata) != IS_UNDEF) {
1472 smart_str_free(&main_metadata_str);
1473 }
1474 php_stream_close(pass.filefp);
1475 if (closeoldfile) {
1476 php_stream_close(oldfile);
1477 }
1478 return EOF;
1479 }
1480
1481 if (FAILURE == phar_zip_applysignature(phar, &pass, &main_metadata_str)) {
1482 goto temperror;
1483 }
1484
1485 /* save zip */
1486 cdir_size = php_stream_tell(pass.centralfp);
1487 cdir_offset = php_stream_tell(pass.filefp);
1488 PHAR_SET_32(eocd.cdir_size, cdir_size);
1489 PHAR_SET_32(eocd.cdir_offset, cdir_offset);
1490 php_stream_seek(pass.centralfp, 0, SEEK_SET);
1491
1492 {
1493 size_t clen;
1494 int ret = php_stream_copy_to_stream_ex(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL, &clen);
1495 if (SUCCESS != ret || clen != cdir_size) {
1496 if (error) {
1497 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
1498 }
1499 goto temperror;
1500 }
1501 }
1502
1503 php_stream_close(pass.centralfp);
1504
1505 if (Z_TYPE(phar->metadata) != IS_UNDEF) {
1506 /* set phar metadata */
1507 PHAR_SET_16(eocd.comment_len, ZSTR_LEN(main_metadata_str.s));
1508
1509 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1510 if (error) {
1511 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1512 }
1513 goto nocentralerror;
1514 }
1515
1516 if (ZSTR_LEN(main_metadata_str.s) != php_stream_write(pass.filefp, ZSTR_VAL(main_metadata_str.s), ZSTR_LEN(main_metadata_str.s))) {
1517 if (error) {
1518 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
1519 }
1520 goto nocentralerror;
1521 }
1522
1523 smart_str_free(&main_metadata_str);
1524
1525 } else {
1526 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1527 if (error) {
1528 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1529 }
1530 goto nocentralerror;
1531 }
1532 }
1533
1534 if (phar->fp && pass.free_fp) {
1535 php_stream_close(phar->fp);
1536 }
1537
1538 if (phar->ufp) {
1539 if (pass.free_ufp) {
1540 php_stream_close(phar->ufp);
1541 }
1542 phar->ufp = NULL;
1543 }
1544
1545 /* re-open */
1546 phar->is_brandnew = 0;
1547
1548 if (phar->donotflush) {
1549 /* deferred flush */
1550 phar->fp = pass.filefp;
1551 } else {
1552 phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1553 if (!phar->fp) {
1554 if (closeoldfile) {
1555 php_stream_close(oldfile);
1556 }
1557 phar->fp = pass.filefp;
1558 if (error) {
1559 spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
1560 }
1561 return EOF;
1562 }
1563 php_stream_rewind(pass.filefp);
1564 php_stream_copy_to_stream_ex(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1565 /* we could also reopen the file in "rb" mode but there is no need for that */
1566 php_stream_close(pass.filefp);
1567 }
1568
1569 if (closeoldfile) {
1570 php_stream_close(oldfile);
1571 }
1572 return EOF;
1573 }
1574 /* }}} */
1575