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