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 phar_parse_metadata_lazy(metadata, &mydata->metadata_tracker, PHAR_GET_16(locator.comment_len), mydata->is_persistent);
268 } else {
269 ZVAL_UNDEF(&mydata->metadata_tracker.val);
270 }
271
272 goto foundit;
273 }
274
275 php_stream_close(fp);
276
277 if (error) {
278 spprintf(error, 4096, "phar error: end of central directory not found in zip-based phar \"%s\"", fname);
279 }
280
281 return FAILURE;
282 foundit:
283 mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
284 #ifdef PHP_WIN32
285 phar_unixify_path_separators(mydata->fname, fname_len);
286 #endif
287 mydata->is_zip = 1;
288 mydata->fname_len = fname_len;
289 ext = strrchr(mydata->fname, '/');
290
291 if (ext) {
292 mydata->ext = memchr(ext, '.', (mydata->fname + fname_len) - ext);
293 if (mydata->ext == ext) {
294 mydata->ext = memchr(ext + 1, '.', (mydata->fname + fname_len) - ext - 1);
295 }
296 if (mydata->ext) {
297 mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
298 }
299 }
300
301 /* clean up on big-endian systems */
302 /* seek to central directory */
303 php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
304 /* read in central directory */
305 zend_hash_init(&mydata->manifest, PHAR_GET_16(locator.count),
306 zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
307 zend_hash_init(&mydata->mounted_dirs, 5,
308 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
309 zend_hash_init(&mydata->virtual_dirs, PHAR_GET_16(locator.count) * 2,
310 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
311 entry.phar = mydata;
312 entry.is_zip = 1;
313 entry.fp_type = PHAR_FP;
314 entry.is_persistent = mydata->is_persistent;
315 #define PHAR_ZIP_FAIL_FREE(errmsg, save) \
316 zend_hash_destroy(&mydata->manifest); \
317 HT_INVALIDATE(&mydata->manifest); \
318 zend_hash_destroy(&mydata->mounted_dirs); \
319 HT_INVALIDATE(&mydata->mounted_dirs); \
320 zend_hash_destroy(&mydata->virtual_dirs); \
321 HT_INVALIDATE(&mydata->virtual_dirs); \
322 php_stream_close(fp); \
323 phar_metadata_tracker_free(&mydata->metadata_tracker, mydata->is_persistent); \
324 if (mydata->signature) { \
325 efree(mydata->signature); \
326 } \
327 if (error) { \
328 spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
329 } \
330 pefree(mydata->fname, mydata->is_persistent); \
331 if (mydata->alias) { \
332 pefree(mydata->alias, mydata->is_persistent); \
333 } \
334 pefree(mydata, mydata->is_persistent); \
335 efree(save); \
336 return FAILURE;
337 #define PHAR_ZIP_FAIL(errmsg) \
338 zend_hash_destroy(&mydata->manifest); \
339 HT_INVALIDATE(&mydata->manifest); \
340 zend_hash_destroy(&mydata->mounted_dirs); \
341 HT_INVALIDATE(&mydata->mounted_dirs); \
342 zend_hash_destroy(&mydata->virtual_dirs); \
343 HT_INVALIDATE(&mydata->virtual_dirs); \
344 php_stream_close(fp); \
345 phar_metadata_tracker_free(&mydata->metadata_tracker, mydata->is_persistent); \
346 if (mydata->signature) { \
347 efree(mydata->signature); \
348 } \
349 if (error) { \
350 spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
351 } \
352 pefree(mydata->fname, mydata->is_persistent); \
353 if (mydata->alias) { \
354 pefree(mydata->alias, mydata->is_persistent); \
355 } \
356 pefree(mydata, mydata->is_persistent); \
357 return FAILURE;
358
359 /* add each central directory item to the manifest */
360 for (i = 0; i < PHAR_GET_16(locator.count); ++i) {
361 phar_zip_central_dir_file zipentry;
362 zend_off_t beforeus = php_stream_tell(fp);
363
364 ZVAL_UNDEF(&entry.metadata_tracker.val);
365 entry.metadata_tracker.str = NULL;
366
367 if (sizeof(zipentry) != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry))) {
368 PHAR_ZIP_FAIL("unable to read central directory entry, truncated");
369 }
370
371 /* clean up for bigendian systems */
372 if (memcmp("PK\1\2", zipentry.signature, 4)) {
373 /* corrupted entry */
374 PHAR_ZIP_FAIL("corrupted central directory entry, no magic signature");
375 }
376
377 if (entry.is_persistent) {
378 entry.manifest_pos = i;
379 }
380
381 entry.compressed_filesize = PHAR_GET_32(zipentry.compsize);
382 entry.uncompressed_filesize = PHAR_GET_32(zipentry.uncompsize);
383 entry.crc32 = PHAR_GET_32(zipentry.crc32);
384 /* do not PHAR_GET_16 either on the next line */
385 entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp);
386 entry.flags = PHAR_ENT_PERM_DEF_FILE;
387 entry.header_offset = PHAR_GET_32(zipentry.offset);
388 entry.offset = entry.offset_abs = PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + PHAR_GET_16(zipentry.filename_len) +
389 PHAR_GET_16(zipentry.extra_len);
390
391 if (PHAR_GET_16(zipentry.flags) & PHAR_ZIP_FLAG_ENCRYPTED) {
392 PHAR_ZIP_FAIL("Cannot process encrypted zip files");
393 }
394
395 if (!PHAR_GET_16(zipentry.filename_len)) {
396 PHAR_ZIP_FAIL("Cannot process zips created from stdin (zero-length filename)");
397 }
398
399 entry.filename_len = PHAR_GET_16(zipentry.filename_len);
400 entry.filename = (char *) pemalloc(entry.filename_len + 1, entry.is_persistent);
401
402 if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) {
403 pefree(entry.filename, entry.is_persistent);
404 PHAR_ZIP_FAIL("unable to read in filename from central directory, truncated");
405 }
406
407 entry.filename[entry.filename_len] = '\0';
408
409 if (entry.filename[entry.filename_len - 1] == '/') {
410 entry.is_dir = 1;
411 if(entry.filename_len > 1) {
412 entry.filename_len--;
413 }
414 entry.flags |= PHAR_ENT_PERM_DEF_DIR;
415 } else {
416 entry.is_dir = 0;
417 }
418
419 if (entry.filename_len == sizeof(".phar/signature.bin")-1 && !strncmp(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
420 size_t read;
421 php_stream *sigfile;
422 char *sig;
423 size_t sig_len;
424
425 pefree(entry.filename, entry.is_persistent);
426
427 if (entry.uncompressed_filesize > 0x10000) {
428 PHAR_ZIP_FAIL("signatures larger than 64 KiB are not supported");
429 }
430
431 php_stream_tell(fp);
432 sigfile = php_stream_fopen_tmpfile();
433 if (!sigfile) {
434 PHAR_ZIP_FAIL("couldn't open temporary file");
435 }
436
437 php_stream_seek(fp, 0, SEEK_SET);
438 /* copy file contents + local headers and zip comment, if any, to be hashed for signature */
439 php_stream_copy_to_stream_ex(fp, sigfile, entry.header_offset, NULL);
440 /* seek to central directory */
441 php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
442 /* copy central directory header */
443 php_stream_copy_to_stream_ex(fp, sigfile, beforeus - PHAR_GET_32(locator.cdir_offset), NULL);
444 if (metadata) {
445 php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len));
446 }
447 php_stream_seek(fp, sizeof(phar_zip_file_header) + entry.header_offset + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
448 sig = (char *) emalloc(entry.uncompressed_filesize);
449 read = php_stream_read(fp, sig, entry.uncompressed_filesize);
450 if (read != entry.uncompressed_filesize || read <= 8) {
451 php_stream_close(sigfile);
452 efree(sig);
453 PHAR_ZIP_FAIL("signature cannot be read");
454 }
455 mydata->sig_flags = PHAR_GET_32(sig);
456 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)) {
457 efree(sig);
458 if (error) {
459 char *save;
460 php_stream_close(sigfile);
461 spprintf(&save, 4096, "signature cannot be verified: %s", *error);
462 efree(*error);
463 PHAR_ZIP_FAIL_FREE(save, save);
464 } else {
465 php_stream_close(sigfile);
466 PHAR_ZIP_FAIL("signature cannot be verified");
467 }
468 }
469 mydata->sig_len = sig_len;
470 php_stream_close(sigfile);
471 efree(sig);
472 /* signature checked out, let's ensure this is the last file in the phar */
473 if (i != PHAR_GET_16(locator.count) - 1) {
474 PHAR_ZIP_FAIL("entries exist after signature, invalid phar");
475 }
476
477 continue;
478 }
479
480 phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len);
481
482 if (PHAR_GET_16(zipentry.extra_len)) {
483 zend_off_t loc = php_stream_tell(fp);
484 if (FAILURE == phar_zip_process_extra(fp, &entry, PHAR_GET_16(zipentry.extra_len))) {
485 pefree(entry.filename, entry.is_persistent);
486 PHAR_ZIP_FAIL("Unable to process extra field header for file in central directory");
487 }
488 php_stream_seek(fp, loc + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
489 }
490
491 switch (PHAR_GET_16(zipentry.compressed)) {
492 case PHAR_ZIP_COMP_NONE :
493 /* compression flag already set */
494 break;
495 case PHAR_ZIP_COMP_DEFLATE :
496 entry.flags |= PHAR_ENT_COMPRESSED_GZ;
497 if (!PHAR_G(has_zlib)) {
498 pefree(entry.filename, entry.is_persistent);
499 PHAR_ZIP_FAIL("zlib extension is required");
500 }
501 break;
502 case PHAR_ZIP_COMP_BZIP2 :
503 entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
504 if (!PHAR_G(has_bz2)) {
505 pefree(entry.filename, entry.is_persistent);
506 PHAR_ZIP_FAIL("bzip2 extension is required");
507 }
508 break;
509 case 1 :
510 pefree(entry.filename, entry.is_persistent);
511 PHAR_ZIP_FAIL("unsupported compression method (Shrunk) used in this zip");
512 case 2 :
513 case 3 :
514 case 4 :
515 case 5 :
516 pefree(entry.filename, entry.is_persistent);
517 PHAR_ZIP_FAIL("unsupported compression method (Reduce) used in this zip");
518 case 6 :
519 pefree(entry.filename, entry.is_persistent);
520 PHAR_ZIP_FAIL("unsupported compression method (Implode) used in this zip");
521 case 7 :
522 pefree(entry.filename, entry.is_persistent);
523 PHAR_ZIP_FAIL("unsupported compression method (Tokenize) used in this zip");
524 case 9 :
525 pefree(entry.filename, entry.is_persistent);
526 PHAR_ZIP_FAIL("unsupported compression method (Deflate64) used in this zip");
527 case 10 :
528 pefree(entry.filename, entry.is_persistent);
529 PHAR_ZIP_FAIL("unsupported compression method (PKWare Implode/old IBM TERSE) used in this zip");
530 case 14 :
531 pefree(entry.filename, entry.is_persistent);
532 PHAR_ZIP_FAIL("unsupported compression method (LZMA) used in this zip");
533 case 18 :
534 pefree(entry.filename, entry.is_persistent);
535 PHAR_ZIP_FAIL("unsupported compression method (IBM TERSE) used in this zip");
536 case 19 :
537 pefree(entry.filename, entry.is_persistent);
538 PHAR_ZIP_FAIL("unsupported compression method (IBM LZ77) used in this zip");
539 case 97 :
540 pefree(entry.filename, entry.is_persistent);
541 PHAR_ZIP_FAIL("unsupported compression method (WavPack) used in this zip");
542 case 98 :
543 pefree(entry.filename, entry.is_persistent);
544 PHAR_ZIP_FAIL("unsupported compression method (PPMd) used in this zip");
545 default :
546 pefree(entry.filename, entry.is_persistent);
547 PHAR_ZIP_FAIL("unsupported compression method (unknown) used in this zip");
548 }
549
550 /* get file metadata */
551 if (PHAR_GET_16(zipentry.comment_len)) {
552 if (PHAR_GET_16(zipentry.comment_len) != php_stream_read(fp, buf, PHAR_GET_16(zipentry.comment_len))) {
553 pefree(entry.filename, entry.is_persistent);
554 PHAR_ZIP_FAIL("unable to read in file comment, truncated");
555 }
556
557 p = buf;
558 phar_parse_metadata_lazy(buf, &entry.metadata_tracker, PHAR_GET_16(zipentry.comment_len), entry.is_persistent);
559 } else {
560 ZVAL_UNDEF(&entry.metadata_tracker.val);
561 }
562
563 if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
564 php_stream_filter *filter;
565 zend_off_t saveloc;
566 /* verify local file header */
567 phar_zip_file_header local;
568
569 /* archive alias found */
570 saveloc = php_stream_tell(fp);
571 php_stream_seek(fp, PHAR_GET_32(zipentry.offset), SEEK_SET);
572
573 if (sizeof(local) != php_stream_read(fp, (char *) &local, sizeof(local))) {
574 pefree(entry.filename, entry.is_persistent);
575 PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (cannot read local file header for alias)");
576 }
577
578 /* verify local header */
579 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)) {
580 pefree(entry.filename, entry.is_persistent);
581 PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (local header of alias does not match central directory)");
582 }
583
584 /* construct actual offset to file start - local extra_len can be different from central extra_len */
585 entry.offset = entry.offset_abs =
586 sizeof(local) + entry.header_offset + PHAR_GET_16(local.filename_len) + PHAR_GET_16(local.extra_len);
587 php_stream_seek(fp, entry.offset, SEEK_SET);
588 /* these next lines should be for php < 5.2.6 after 5.3 filters are fixed */
589 fp->writepos = 0;
590 fp->readpos = 0;
591 php_stream_seek(fp, entry.offset, SEEK_SET);
592 fp->writepos = 0;
593 fp->readpos = 0;
594 /* the above lines should be for php < 5.2.6 after 5.3 filters are fixed */
595
596 mydata->alias_len = entry.uncompressed_filesize;
597 if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
598 filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp));
599
600 if (!filter) {
601 pefree(entry.filename, entry.is_persistent);
602 PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed");
603 }
604
605 php_stream_filter_append(&fp->readfilters, filter);
606
607 // TODO: refactor to avoid reallocation ???
608 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
609 {
610 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
611 if (str) {
612 entry.uncompressed_filesize = ZSTR_LEN(str);
613 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
614 zend_string_release_ex(str, 0);
615 } else {
616 actual_alias = NULL;
617 entry.uncompressed_filesize = 0;
618 }
619 }
620
621 if (!entry.uncompressed_filesize || !actual_alias) {
622 pefree(entry.filename, entry.is_persistent);
623 PHAR_ZIP_FAIL("unable to read in alias, truncated");
624 }
625
626 php_stream_filter_flush(filter, 1);
627 php_stream_filter_remove(filter, 1);
628
629 } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
630 filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp));
631
632 if (!filter) {
633 pefree(entry.filename, entry.is_persistent);
634 PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed");
635 }
636
637 php_stream_filter_append(&fp->readfilters, filter);
638
639 // TODO: refactor to avoid reallocation ???
640 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
641 {
642 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
643 if (str) {
644 entry.uncompressed_filesize = ZSTR_LEN(str);
645 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
646 zend_string_release_ex(str, 0);
647 } else {
648 actual_alias = NULL;
649 entry.uncompressed_filesize = 0;
650 }
651 }
652
653 if (!entry.uncompressed_filesize || !actual_alias) {
654 pefree(entry.filename, entry.is_persistent);
655 PHAR_ZIP_FAIL("unable to read in alias, truncated");
656 }
657
658 php_stream_filter_flush(filter, 1);
659 php_stream_filter_remove(filter, 1);
660 } else {
661 // TODO: refactor to avoid reallocation ???
662 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
663 {
664 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
665 if (str) {
666 entry.uncompressed_filesize = ZSTR_LEN(str);
667 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
668 zend_string_release_ex(str, 0);
669 } else {
670 actual_alias = NULL;
671 entry.uncompressed_filesize = 0;
672 }
673 }
674
675 if (!entry.uncompressed_filesize || !actual_alias) {
676 pefree(entry.filename, entry.is_persistent);
677 PHAR_ZIP_FAIL("unable to read in alias, truncated");
678 }
679 }
680
681 /* return to central directory parsing */
682 php_stream_seek(fp, saveloc, SEEK_SET);
683 }
684
685 phar_set_inode(&entry);
686 zend_hash_str_add_mem(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry, sizeof(phar_entry_info));
687 }
688
689 if (zend_hash_str_exists(&(mydata->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
690 mydata->is_data = 0;
691 } else {
692 mydata->is_data = 1;
693 }
694
695 /* ensure signature set */
696 if (!mydata->is_data && PHAR_G(require_hash) && !mydata->signature) {
697 PHAR_ZIP_FAIL("signature is missing");
698 }
699
700 mydata->fp = fp;
701
702 zend_hash_str_add_ptr(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len, mydata);
703
704 if (actual_alias) {
705 phar_archive_data *fd_ptr;
706
707 if (!phar_validate_alias(actual_alias, mydata->alias_len)) {
708 if (error) {
709 spprintf(error, 4096, "phar error: invalid alias \"%s\" in zip-based phar \"%s\"", actual_alias, fname);
710 }
711 efree(actual_alias);
712 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
713 return FAILURE;
714 }
715
716 mydata->is_temporary_alias = 0;
717
718 if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), actual_alias, mydata->alias_len))) {
719 if (SUCCESS != phar_free_alias(fd_ptr, actual_alias, mydata->alias_len)) {
720 if (error) {
721 spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with implicit alias, alias is already in use", fname);
722 }
723 efree(actual_alias);
724 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
725 return FAILURE;
726 }
727 }
728
729 mydata->alias = entry.is_persistent ? pestrndup(actual_alias, mydata->alias_len, 1) : actual_alias;
730
731 if (entry.is_persistent) {
732 efree(actual_alias);
733 }
734
735 zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), mydata->alias, mydata->alias_len, mydata);
736 } else {
737 phar_archive_data *fd_ptr;
738
739 if (alias_len) {
740 if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
741 if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
742 if (error) {
743 spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with explicit alias, alias is already in use", fname);
744 }
745 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
746 return FAILURE;
747 }
748 }
749
750 zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), actual_alias, mydata->alias_len, mydata);
751 mydata->alias = pestrndup(alias, alias_len, mydata->is_persistent);
752 mydata->alias_len = alias_len;
753 } else {
754 mydata->alias = pestrndup(mydata->fname, fname_len, mydata->is_persistent);
755 mydata->alias_len = fname_len;
756 }
757
758 mydata->is_temporary_alias = 1;
759 }
760
761 if (pphar) {
762 *pphar = mydata;
763 }
764
765 return SUCCESS;
766 }
767 /* }}} */
768
769 /**
770 * Create or open a zip-based phar for writing
771 */
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)772 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) /* {{{ */
773 {
774 phar_archive_data *phar;
775 int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error);
776
777 if (FAILURE == ret) {
778 return FAILURE;
779 }
780
781 if (pphar) {
782 *pphar = phar;
783 }
784
785 phar->is_data = is_data;
786
787 if (phar->is_zip) {
788 return ret;
789 }
790
791 if (phar->is_brandnew) {
792 phar->internal_file_start = 0;
793 phar->is_zip = 1;
794 phar->is_tar = 0;
795 return SUCCESS;
796 }
797
798 /* we've reached here - the phar exists and is a regular phar */
799 if (error) {
800 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);
801 }
802
803 return FAILURE;
804 }
805 /* }}} */
806
807 struct _phar_zip_pass {
808 php_stream *filefp;
809 php_stream *centralfp;
810 php_stream *old;
811 int free_fp;
812 int free_ufp;
813 char **error;
814 };
815 /* 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)816 static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ */
817 {
818 phar_zip_file_header local;
819 phar_zip_unix3 perms;
820 phar_zip_central_dir_file central;
821 struct _phar_zip_pass *p;
822 uint32_t newcrc32;
823 zend_off_t offset;
824 int not_really_modified = 0;
825 p = (struct _phar_zip_pass*) arg;
826 uint16_t general_purpose_flags;
827
828 if (entry->is_mounted) {
829 return ZEND_HASH_APPLY_KEEP;
830 }
831
832 if (entry->is_deleted) {
833 if (entry->fp_refcount <= 0) {
834 return ZEND_HASH_APPLY_REMOVE;
835 } else {
836 /* we can't delete this in-memory until it is closed */
837 return ZEND_HASH_APPLY_KEEP;
838 }
839 }
840
841 phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len);
842 memset(&local, 0, sizeof(local));
843 memset(¢ral, 0, sizeof(central));
844 memset(&perms, 0, sizeof(perms));
845 memcpy(local.signature, "PK\3\4", 4);
846 memcpy(central.signature, "PK\1\2", 4);
847 PHAR_SET_16(central.extra_len, sizeof(perms));
848 PHAR_SET_16(local.extra_len, sizeof(perms));
849 perms.tag[0] = 'n';
850 perms.tag[1] = 'u';
851 PHAR_SET_16(perms.size, sizeof(perms) - 4);
852 PHAR_SET_16(perms.perms, entry->flags & PHAR_ENT_PERM_MASK);
853 {
854 uint32_t crc = (uint32_t) ~0;
855 CRC32(crc, perms.perms[0]);
856 CRC32(crc, perms.perms[1]);
857 PHAR_SET_32(perms.crc32, ~crc);
858 }
859
860 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
861 PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_DEFLATE);
862 PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_DEFLATE);
863 }
864
865 if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
866 PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_BZIP2);
867 PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_BZIP2);
868 }
869
870 /* do not use PHAR_GET_16 on either field of the next line */
871 phar_zip_u2d_time(entry->timestamp, local.timestamp, local.datestamp);
872 memcpy(central.timestamp, local.timestamp, sizeof(local.timestamp));
873 memcpy(central.datestamp, local.datestamp, sizeof(local.datestamp));
874 PHAR_SET_16(central.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
875 PHAR_SET_16(local.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
876 // set language encoding flag (all filenames have to be UTF-8 anyway)
877 general_purpose_flags = PHAR_GET_16(central.flags);
878 PHAR_SET_16(central.flags, general_purpose_flags | (1 << 11));
879 general_purpose_flags = PHAR_GET_16(local.flags);
880 PHAR_SET_16(local.flags, general_purpose_flags | (1 << 11));
881 PHAR_SET_32(central.offset, php_stream_tell(p->filefp));
882
883 /* do extra field for perms later */
884 if (entry->is_modified) {
885 uint32_t loc;
886 php_stream_filter *filter;
887 php_stream *efp;
888
889 if (entry->is_dir) {
890 entry->is_modified = 0;
891 if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
892 php_stream_close(entry->fp);
893 entry->fp = NULL;
894 entry->fp_type = PHAR_FP;
895 }
896 goto continue_dir;
897 }
898
899 if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
900 spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
901 return ZEND_HASH_APPLY_STOP;
902 }
903
904 /* we can be modified and already be compressed, such as when chmod() is executed */
905 if (entry->flags & PHAR_ENT_COMPRESSION_MASK && (entry->old_flags == entry->flags || !entry->old_flags)) {
906 not_really_modified = 1;
907 goto is_compressed;
908 }
909
910 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
911 spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
912 return ZEND_HASH_APPLY_STOP;
913 }
914
915 efp = phar_get_efp(entry, 0);
916 newcrc32 = ~0;
917
918 for (loc = 0;loc < entry->uncompressed_filesize; ++loc) {
919 CRC32(newcrc32, php_stream_getc(efp));
920 }
921
922 entry->crc32 = ~newcrc32;
923 PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
924 PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
925
926 if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
927 /* not compressed */
928 entry->compressed_filesize = entry->uncompressed_filesize;
929 PHAR_SET_32(central.compsize, entry->uncompressed_filesize);
930 PHAR_SET_32(local.compsize, entry->uncompressed_filesize);
931 goto not_compressed;
932 }
933
934 filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
935
936 if (!filter) {
937 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
938 spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
939 } else {
940 spprintf(p->error, 0, "unable to bzip2 compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
941 }
942 return ZEND_HASH_APPLY_STOP;
943 }
944
945 /* create new file that holds the compressed version */
946 /* work around inability to specify freedom in write and strictness
947 in read count */
948 entry->cfp = php_stream_fopen_tmpfile();
949
950 if (!entry->cfp) {
951 spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
952 return ZEND_HASH_APPLY_STOP;
953 }
954
955 php_stream_flush(efp);
956
957 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
958 spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
959 return ZEND_HASH_APPLY_STOP;
960 }
961
962 php_stream_filter_append((&entry->cfp->writefilters), filter);
963
964 if (SUCCESS != php_stream_copy_to_stream_ex(efp, entry->cfp, entry->uncompressed_filesize, NULL)) {
965 spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname);
966 return ZEND_HASH_APPLY_STOP;
967 }
968
969 php_stream_filter_flush(filter, 1);
970 php_stream_flush(entry->cfp);
971 php_stream_filter_remove(filter, 1);
972 php_stream_seek(entry->cfp, 0, SEEK_END);
973 entry->compressed_filesize = (uint32_t) php_stream_tell(entry->cfp);
974 PHAR_SET_32(central.compsize, entry->compressed_filesize);
975 PHAR_SET_32(local.compsize, entry->compressed_filesize);
976 /* generate crc on compressed file */
977 php_stream_rewind(entry->cfp);
978 entry->old_flags = entry->flags;
979 entry->is_modified = 1;
980 } else {
981 is_compressed:
982 PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
983 PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
984 PHAR_SET_32(central.compsize, entry->compressed_filesize);
985 PHAR_SET_32(local.compsize, entry->compressed_filesize);
986 if (p->old) {
987 if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) {
988 spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
989 return ZEND_HASH_APPLY_STOP;
990 }
991 }
992 }
993 not_compressed:
994 PHAR_SET_32(central.crc32, entry->crc32);
995 PHAR_SET_32(local.crc32, entry->crc32);
996 continue_dir:
997 /* set file metadata */
998 if (phar_metadata_tracker_has_data(&entry->metadata_tracker, entry->is_persistent)) {
999 phar_metadata_tracker_try_ensure_has_serialized_data(&entry->metadata_tracker, entry->is_persistent);
1000 PHAR_SET_16(central.comment_len, entry->metadata_tracker.str ? ZSTR_LEN(entry->metadata_tracker.str) : 0);
1001 }
1002
1003 entry->header_offset = php_stream_tell(p->filefp);
1004 offset = entry->header_offset + sizeof(local) + entry->filename_len + (entry->is_dir ? 1 : 0) + sizeof(perms);
1005
1006 if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) {
1007 spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1008 return ZEND_HASH_APPLY_STOP;
1009 }
1010
1011 if (sizeof(central) != php_stream_write(p->centralfp, (char *)¢ral, sizeof(central))) {
1012 spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1013 return ZEND_HASH_APPLY_STOP;
1014 }
1015
1016 if (entry->is_dir) {
1017 if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
1018 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);
1019 return ZEND_HASH_APPLY_STOP;
1020 }
1021
1022 if (1 != php_stream_write(p->filefp, "/", 1)) {
1023 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);
1024 return ZEND_HASH_APPLY_STOP;
1025 }
1026
1027 if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1028 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);
1029 return ZEND_HASH_APPLY_STOP;
1030 }
1031
1032 if (1 != php_stream_write(p->centralfp, "/", 1)) {
1033 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);
1034 return ZEND_HASH_APPLY_STOP;
1035 }
1036 } else {
1037 if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
1038 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);
1039 return ZEND_HASH_APPLY_STOP;
1040 }
1041
1042 if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1043 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);
1044 return ZEND_HASH_APPLY_STOP;
1045 }
1046 }
1047
1048 if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) {
1049 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);
1050 return ZEND_HASH_APPLY_STOP;
1051 }
1052
1053 if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) {
1054 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);
1055 return ZEND_HASH_APPLY_STOP;
1056 }
1057
1058 if (!not_really_modified && entry->is_modified) {
1059 if (entry->cfp) {
1060 if (SUCCESS != php_stream_copy_to_stream_ex(entry->cfp, p->filefp, entry->compressed_filesize, NULL)) {
1061 spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1062 return ZEND_HASH_APPLY_STOP;
1063 }
1064
1065 php_stream_close(entry->cfp);
1066 entry->cfp = NULL;
1067 } else {
1068 if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
1069 return ZEND_HASH_APPLY_STOP;
1070 }
1071
1072 phar_seek_efp(entry, 0, SEEK_SET, 0, 0);
1073
1074 if (SUCCESS != php_stream_copy_to_stream_ex(phar_get_efp(entry, 0), p->filefp, entry->uncompressed_filesize, NULL)) {
1075 spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1076 return ZEND_HASH_APPLY_STOP;
1077 }
1078 }
1079
1080 if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) {
1081 php_stream_close(entry->fp);
1082 }
1083
1084 entry->is_modified = 0;
1085 } else {
1086 entry->is_modified = 0;
1087 if (entry->fp_refcount) {
1088 /* open file pointers refer to this fp, do not free the stream */
1089 switch (entry->fp_type) {
1090 case PHAR_FP:
1091 p->free_fp = 0;
1092 break;
1093 case PHAR_UFP:
1094 p->free_ufp = 0;
1095 default:
1096 break;
1097 }
1098 }
1099
1100 if (!entry->is_dir && entry->compressed_filesize && SUCCESS != php_stream_copy_to_stream_ex(p->old, p->filefp, entry->compressed_filesize, NULL)) {
1101 spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1102 return ZEND_HASH_APPLY_STOP;
1103 }
1104 }
1105
1106 entry->fp = NULL;
1107 entry->offset = entry->offset_abs = offset;
1108 entry->fp_type = PHAR_FP;
1109
1110 if (entry->metadata_tracker.str) {
1111 if (ZSTR_LEN(entry->metadata_tracker.str) != php_stream_write(p->centralfp, ZSTR_VAL(entry->metadata_tracker.str), ZSTR_LEN(entry->metadata_tracker.str))) {
1112 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);
1113 return ZEND_HASH_APPLY_STOP;
1114 }
1115 }
1116
1117 return ZEND_HASH_APPLY_KEEP;
1118 }
1119 /* }}} */
1120
phar_zip_changed_apply(zval * zv,void * arg)1121 static int phar_zip_changed_apply(zval *zv, void *arg) /* {{{ */
1122 {
1123 return phar_zip_changed_apply_int(Z_PTR_P(zv), arg);
1124 }
1125 /* }}} */
1126
phar_zip_applysignature(phar_archive_data * phar,struct _phar_zip_pass * pass)1127 static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pass *pass) /* {{{ */
1128 {
1129 /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
1130 if (!phar->is_data || phar->sig_flags) {
1131 size_t signature_length;
1132 char *signature, sigbuf[8];
1133 phar_entry_info entry = {0};
1134 php_stream *newfile;
1135 zend_off_t tell;
1136
1137 newfile = php_stream_fopen_tmpfile();
1138 if (newfile == NULL) {
1139 spprintf(pass->error, 0, "phar error: unable to create temporary file for the signature file");
1140 return FAILURE;
1141 }
1142 tell = php_stream_tell(pass->filefp);
1143 /* copy the local files, central directory, and the zip comment to generate the hash */
1144 php_stream_seek(pass->filefp, 0, SEEK_SET);
1145 php_stream_copy_to_stream_ex(pass->filefp, newfile, tell, NULL);
1146 tell = php_stream_tell(pass->centralfp);
1147 php_stream_seek(pass->centralfp, 0, SEEK_SET);
1148 php_stream_copy_to_stream_ex(pass->centralfp, newfile, tell, NULL);
1149 if (phar->metadata_tracker.str) {
1150 php_stream_write(newfile, ZSTR_VAL(phar->metadata_tracker.str), ZSTR_LEN(phar->metadata_tracker.str));
1151 }
1152
1153 if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, pass->error)) {
1154 if (pass->error) {
1155 char *save = *(pass->error);
1156 spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", save);
1157 efree(save);
1158 }
1159
1160 php_stream_close(newfile);
1161 return FAILURE;
1162 }
1163
1164 entry.filename = ".phar/signature.bin";
1165 entry.filename_len = sizeof(".phar/signature.bin")-1;
1166 entry.fp = php_stream_fopen_tmpfile();
1167 entry.fp_type = PHAR_MOD;
1168 entry.is_modified = 1;
1169 if (entry.fp == NULL) {
1170 spprintf(pass->error, 0, "phar error: unable to create temporary file for signature");
1171 return FAILURE;
1172 }
1173
1174 PHAR_SET_32(sigbuf, phar->sig_flags);
1175 PHAR_SET_32(sigbuf + 4, signature_length);
1176
1177 if (Z_UL(8) != php_stream_write(entry.fp, sigbuf, 8) || signature_length != php_stream_write(entry.fp, signature, signature_length)) {
1178 efree(signature);
1179 if (pass->error) {
1180 spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar %s", phar->fname);
1181 }
1182
1183 php_stream_close(newfile);
1184 return FAILURE;
1185 }
1186
1187 efree(signature);
1188 entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
1189 entry.phar = phar;
1190 /* throw out return value and write the signature */
1191 phar_zip_changed_apply_int(&entry, (void *)pass);
1192 php_stream_close(newfile);
1193
1194 if (pass->error && *(pass->error)) {
1195 /* error is set by writeheaders */
1196 return FAILURE;
1197 }
1198 } /* signature */
1199 return SUCCESS;
1200 }
1201 /* }}} */
1202
phar_zip_flush(phar_archive_data * phar,char * user_stub,zend_long len,int defaultstub,char ** error)1203 int phar_zip_flush(phar_archive_data *phar, char *user_stub, zend_long len, int defaultstub, char **error) /* {{{ */
1204 {
1205 char *pos;
1206 static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
1207 char halt_stub[] = "__HALT_COMPILER();";
1208 char *tmp;
1209
1210 php_stream *stubfile, *oldfile;
1211 int free_user_stub, closeoldfile = 0;
1212 phar_entry_info entry = {0};
1213 char *temperr = NULL;
1214 struct _phar_zip_pass pass;
1215 phar_zip_dir_end eocd;
1216 uint32_t cdir_size, cdir_offset;
1217
1218 pass.error = &temperr;
1219 entry.flags = PHAR_ENT_PERM_DEF_FILE;
1220 entry.timestamp = time(NULL);
1221 entry.is_modified = 1;
1222 entry.is_zip = 1;
1223 entry.phar = phar;
1224 entry.fp_type = PHAR_MOD;
1225
1226 if (phar->is_persistent) {
1227 if (error) {
1228 spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
1229 }
1230 return EOF;
1231 }
1232
1233 if (phar->is_data) {
1234 goto nostub;
1235 }
1236
1237 /* set alias */
1238 if (!phar->is_temporary_alias && phar->alias_len) {
1239 entry.fp = php_stream_fopen_tmpfile();
1240 if (entry.fp == NULL) {
1241 spprintf(error, 0, "phar error: unable to create temporary file");
1242 return EOF;
1243 }
1244 if (phar->alias_len != php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
1245 if (error) {
1246 spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1247 }
1248 return EOF;
1249 }
1250
1251 entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len;
1252 entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1253 entry.filename_len = sizeof(".phar/alias.txt")-1;
1254
1255 zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1256 } else {
1257 zend_hash_str_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1258 }
1259
1260 /* register alias */
1261 if (phar->alias_len) {
1262 if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error)) {
1263 return EOF;
1264 }
1265 }
1266
1267 /* set stub */
1268 if (user_stub && !defaultstub) {
1269 if (len < 0) {
1270 /* resource passed in */
1271 if (!(php_stream_from_zval_no_verify(stubfile, (zval *)user_stub))) {
1272 if (error) {
1273 spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1274 }
1275 return EOF;
1276 }
1277
1278 if (len == -1) {
1279 len = PHP_STREAM_COPY_ALL;
1280 } else {
1281 len = -len;
1282 }
1283
1284 user_stub = 0;
1285
1286 // TODO: refactor to avoid reallocation ???
1287 //??? len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)
1288 {
1289 zend_string *str = php_stream_copy_to_mem(stubfile, len, 0);
1290 if (str) {
1291 len = ZSTR_LEN(str);
1292 user_stub = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
1293 zend_string_release_ex(str, 0);
1294 } else {
1295 user_stub = NULL;
1296 len = 0;
1297 }
1298 }
1299
1300 if (!len || !user_stub) {
1301 if (error) {
1302 spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1303 }
1304 return EOF;
1305 }
1306 free_user_stub = 1;
1307 } else {
1308 free_user_stub = 0;
1309 }
1310
1311 tmp = estrndup(user_stub, len);
1312 if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
1313 efree(tmp);
1314 if (error) {
1315 spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
1316 }
1317 if (free_user_stub) {
1318 efree(user_stub);
1319 }
1320 return EOF;
1321 }
1322 pos = user_stub + (pos - tmp);
1323 efree(tmp);
1324
1325 len = pos - user_stub + 18;
1326 entry.fp = php_stream_fopen_tmpfile();
1327 if (entry.fp == NULL) {
1328 spprintf(error, 0, "phar error: unable to create temporary file");
1329 return EOF;
1330 }
1331 entry.uncompressed_filesize = len + 5;
1332
1333 if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
1334 || 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
1335 if (error) {
1336 spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
1337 }
1338 if (free_user_stub) {
1339 efree(user_stub);
1340 }
1341 php_stream_close(entry.fp);
1342 return EOF;
1343 }
1344
1345 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1346 entry.filename_len = sizeof(".phar/stub.php")-1;
1347
1348 zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1349
1350 if (free_user_stub) {
1351 efree(user_stub);
1352 }
1353 } else {
1354 /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1355 entry.fp = php_stream_fopen_tmpfile();
1356 if (entry.fp == NULL) {
1357 spprintf(error, 0, "phar error: unable to create temporary file");
1358 return EOF;
1359 }
1360 if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1361 php_stream_close(entry.fp);
1362 if (error) {
1363 spprintf(error, 0, "unable to %s stub in%szip-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1364 }
1365 return EOF;
1366 }
1367
1368 entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1369 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1370 entry.filename_len = sizeof(".phar/stub.php")-1;
1371
1372 if (!defaultstub) {
1373 if (!zend_hash_str_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1374 if (NULL == zend_hash_str_add_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1375 php_stream_close(entry.fp);
1376 efree(entry.filename);
1377 if (error) {
1378 spprintf(error, 0, "unable to create stub in zip-based phar \"%s\"", phar->fname);
1379 }
1380 return EOF;
1381 }
1382 } else {
1383 php_stream_close(entry.fp);
1384 efree(entry.filename);
1385 }
1386 } else {
1387 zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1388 }
1389 }
1390 nostub:
1391 if (phar->fp && !phar->is_brandnew) {
1392 oldfile = phar->fp;
1393 closeoldfile = 0;
1394 php_stream_rewind(oldfile);
1395 } else {
1396 oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1397 closeoldfile = oldfile != NULL;
1398 }
1399
1400 /* save modified files to the zip */
1401 pass.old = oldfile;
1402 pass.filefp = php_stream_fopen_tmpfile();
1403
1404 if (!pass.filefp) {
1405 fperror:
1406 if (closeoldfile) {
1407 php_stream_close(oldfile);
1408 }
1409 if (error) {
1410 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
1411 }
1412 return EOF;
1413 }
1414
1415 pass.centralfp = php_stream_fopen_tmpfile();
1416
1417 if (!pass.centralfp) {
1418 goto fperror;
1419 }
1420
1421 pass.free_fp = pass.free_ufp = 1;
1422 memset(&eocd, 0, sizeof(eocd));
1423
1424 memcpy(eocd.signature, "PK\5\6", 4);
1425 if (!phar->is_data && !phar->sig_flags) {
1426 phar->sig_flags = PHAR_SIG_SHA1;
1427 }
1428 if (phar->sig_flags) {
1429 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest) + 1);
1430 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest) + 1);
1431 } else {
1432 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
1433 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
1434 }
1435 zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass);
1436
1437 phar_metadata_tracker_try_ensure_has_serialized_data(&phar->metadata_tracker, phar->is_persistent);
1438 if (temperr) {
1439 if (error) {
1440 spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
1441 }
1442 efree(temperr);
1443 temperror:
1444 php_stream_close(pass.centralfp);
1445 nocentralerror:
1446 php_stream_close(pass.filefp);
1447 if (closeoldfile) {
1448 php_stream_close(oldfile);
1449 }
1450 return EOF;
1451 }
1452
1453 if (FAILURE == phar_zip_applysignature(phar, &pass)) {
1454 goto temperror;
1455 }
1456
1457 /* save zip */
1458 cdir_size = php_stream_tell(pass.centralfp);
1459 cdir_offset = php_stream_tell(pass.filefp);
1460 PHAR_SET_32(eocd.cdir_size, cdir_size);
1461 PHAR_SET_32(eocd.cdir_offset, cdir_offset);
1462 php_stream_seek(pass.centralfp, 0, SEEK_SET);
1463
1464 {
1465 size_t clen;
1466 int ret = php_stream_copy_to_stream_ex(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL, &clen);
1467 if (SUCCESS != ret || clen != cdir_size) {
1468 if (error) {
1469 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
1470 }
1471 goto temperror;
1472 }
1473 }
1474
1475 php_stream_close(pass.centralfp);
1476
1477 phar_metadata_tracker_try_ensure_has_serialized_data(&phar->metadata_tracker, phar->is_persistent);
1478 if (phar->metadata_tracker.str) {
1479 /* set phar metadata */
1480 PHAR_SET_16(eocd.comment_len, ZSTR_LEN(phar->metadata_tracker.str));
1481
1482 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1483 if (error) {
1484 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1485 }
1486 goto nocentralerror;
1487 }
1488
1489 if (ZSTR_LEN(phar->metadata_tracker.str) != php_stream_write(pass.filefp, ZSTR_VAL(phar->metadata_tracker.str), ZSTR_LEN(phar->metadata_tracker.str))) {
1490 if (error) {
1491 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
1492 }
1493 goto nocentralerror;
1494 }
1495 } else {
1496 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1497 if (error) {
1498 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1499 }
1500 goto nocentralerror;
1501 }
1502 }
1503
1504 if (phar->fp && pass.free_fp) {
1505 php_stream_close(phar->fp);
1506 }
1507
1508 if (phar->ufp) {
1509 if (pass.free_ufp) {
1510 php_stream_close(phar->ufp);
1511 }
1512 phar->ufp = NULL;
1513 }
1514
1515 /* re-open */
1516 phar->is_brandnew = 0;
1517
1518 if (phar->donotflush) {
1519 /* deferred flush */
1520 phar->fp = pass.filefp;
1521 } else {
1522 phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1523 if (!phar->fp) {
1524 if (closeoldfile) {
1525 php_stream_close(oldfile);
1526 }
1527 phar->fp = pass.filefp;
1528 if (error) {
1529 spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
1530 }
1531 return EOF;
1532 }
1533 php_stream_rewind(pass.filefp);
1534 php_stream_copy_to_stream_ex(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1535 /* we could also reopen the file in "rb" mode but there is no need for that */
1536 php_stream_close(pass.filefp);
1537 }
1538
1539 if (closeoldfile) {
1540 php_stream_close(oldfile);
1541 }
1542 return EOF;
1543 }
1544 /* }}} */
1545