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 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, (bool)mydata->is_persistent);
307 zend_hash_init(&mydata->mounted_dirs, 5,
308 zend_get_hash_value, NULL, (bool)mydata->is_persistent);
309 zend_hash_init(&mydata->virtual_dirs, PHAR_GET_16(locator.count) * 2,
310 zend_get_hash_value, NULL, (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) php_crc32_bulk_init();
855 CRC32(crc, perms.perms[0]);
856 CRC32(crc, perms.perms[1]);
857 PHAR_SET_32(perms.crc32, php_crc32_bulk_end(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 php_stream_filter *filter;
886 php_stream *efp;
887
888 if (entry->is_dir) {
889 entry->is_modified = 0;
890 if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
891 php_stream_close(entry->fp);
892 entry->fp = NULL;
893 entry->fp_type = PHAR_FP;
894 }
895 goto continue_dir;
896 }
897
898 if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
899 spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
900 return ZEND_HASH_APPLY_STOP;
901 }
902
903 /* we can be modified and already be compressed, such as when chmod() is executed */
904 if (entry->flags & PHAR_ENT_COMPRESSION_MASK && (entry->old_flags == entry->flags || !entry->old_flags)) {
905 not_really_modified = 1;
906 goto is_compressed;
907 }
908
909 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
910 spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
911 return ZEND_HASH_APPLY_STOP;
912 }
913
914 efp = phar_get_efp(entry, 0);
915 newcrc32 = php_crc32_bulk_init();
916
917 php_crc32_stream_bulk_update(&newcrc32, efp, entry->uncompressed_filesize);
918
919 entry->crc32 = php_crc32_bulk_end(newcrc32);
920 PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
921 PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
922
923 if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
924 /* not compressed */
925 entry->compressed_filesize = entry->uncompressed_filesize;
926 PHAR_SET_32(central.compsize, entry->uncompressed_filesize);
927 PHAR_SET_32(local.compsize, entry->uncompressed_filesize);
928 goto not_compressed;
929 }
930
931 filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
932
933 if (!filter) {
934 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
935 spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
936 } else {
937 spprintf(p->error, 0, "unable to bzip2 compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
938 }
939 return ZEND_HASH_APPLY_STOP;
940 }
941
942 /* create new file that holds the compressed version */
943 /* work around inability to specify freedom in write and strictness
944 in read count */
945 entry->cfp = php_stream_fopen_tmpfile();
946
947 if (!entry->cfp) {
948 spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
949 return ZEND_HASH_APPLY_STOP;
950 }
951
952 php_stream_flush(efp);
953
954 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
955 spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
956 return ZEND_HASH_APPLY_STOP;
957 }
958
959 php_stream_filter_append((&entry->cfp->writefilters), filter);
960
961 if (SUCCESS != php_stream_copy_to_stream_ex(efp, entry->cfp, entry->uncompressed_filesize, NULL)) {
962 spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname);
963 return ZEND_HASH_APPLY_STOP;
964 }
965
966 php_stream_filter_flush(filter, 1);
967 php_stream_flush(entry->cfp);
968 php_stream_filter_remove(filter, 1);
969 php_stream_seek(entry->cfp, 0, SEEK_END);
970 entry->compressed_filesize = (uint32_t) php_stream_tell(entry->cfp);
971 PHAR_SET_32(central.compsize, entry->compressed_filesize);
972 PHAR_SET_32(local.compsize, entry->compressed_filesize);
973 /* generate crc on compressed file */
974 php_stream_rewind(entry->cfp);
975 entry->old_flags = entry->flags;
976 entry->is_modified = 1;
977 } else {
978 is_compressed:
979 PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
980 PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
981 PHAR_SET_32(central.compsize, entry->compressed_filesize);
982 PHAR_SET_32(local.compsize, entry->compressed_filesize);
983 if (p->old) {
984 if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) {
985 spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
986 return ZEND_HASH_APPLY_STOP;
987 }
988 }
989 }
990 not_compressed:
991 PHAR_SET_32(central.crc32, entry->crc32);
992 PHAR_SET_32(local.crc32, entry->crc32);
993 continue_dir:
994 /* set file metadata */
995 if (phar_metadata_tracker_has_data(&entry->metadata_tracker, entry->is_persistent)) {
996 phar_metadata_tracker_try_ensure_has_serialized_data(&entry->metadata_tracker, entry->is_persistent);
997 PHAR_SET_16(central.comment_len, entry->metadata_tracker.str ? ZSTR_LEN(entry->metadata_tracker.str) : 0);
998 }
999
1000 entry->header_offset = php_stream_tell(p->filefp);
1001 offset = entry->header_offset + sizeof(local) + entry->filename_len + (entry->is_dir ? 1 : 0) + sizeof(perms);
1002
1003 if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) {
1004 spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1005 return ZEND_HASH_APPLY_STOP;
1006 }
1007
1008 if (sizeof(central) != php_stream_write(p->centralfp, (char *)¢ral, sizeof(central))) {
1009 spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1010 return ZEND_HASH_APPLY_STOP;
1011 }
1012
1013 if (entry->is_dir) {
1014 if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
1015 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);
1016 return ZEND_HASH_APPLY_STOP;
1017 }
1018
1019 if (1 != php_stream_write(p->filefp, "/", 1)) {
1020 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);
1021 return ZEND_HASH_APPLY_STOP;
1022 }
1023
1024 if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1025 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);
1026 return ZEND_HASH_APPLY_STOP;
1027 }
1028
1029 if (1 != php_stream_write(p->centralfp, "/", 1)) {
1030 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);
1031 return ZEND_HASH_APPLY_STOP;
1032 }
1033 } else {
1034 if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
1035 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);
1036 return ZEND_HASH_APPLY_STOP;
1037 }
1038
1039 if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1040 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);
1041 return ZEND_HASH_APPLY_STOP;
1042 }
1043 }
1044
1045 if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) {
1046 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);
1047 return ZEND_HASH_APPLY_STOP;
1048 }
1049
1050 if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) {
1051 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);
1052 return ZEND_HASH_APPLY_STOP;
1053 }
1054
1055 if (!not_really_modified && entry->is_modified) {
1056 if (entry->cfp) {
1057 if (SUCCESS != php_stream_copy_to_stream_ex(entry->cfp, p->filefp, entry->compressed_filesize, NULL)) {
1058 spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1059 return ZEND_HASH_APPLY_STOP;
1060 }
1061
1062 php_stream_close(entry->cfp);
1063 entry->cfp = NULL;
1064 } else {
1065 if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
1066 return ZEND_HASH_APPLY_STOP;
1067 }
1068
1069 phar_seek_efp(entry, 0, SEEK_SET, 0, 0);
1070
1071 if (SUCCESS != php_stream_copy_to_stream_ex(phar_get_efp(entry, 0), p->filefp, entry->uncompressed_filesize, NULL)) {
1072 spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1073 return ZEND_HASH_APPLY_STOP;
1074 }
1075 }
1076
1077 if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) {
1078 php_stream_close(entry->fp);
1079 }
1080
1081 entry->is_modified = 0;
1082 } else {
1083 entry->is_modified = 0;
1084 if (entry->fp_refcount) {
1085 /* open file pointers refer to this fp, do not free the stream */
1086 switch (entry->fp_type) {
1087 case PHAR_FP:
1088 p->free_fp = 0;
1089 break;
1090 case PHAR_UFP:
1091 p->free_ufp = 0;
1092 default:
1093 break;
1094 }
1095 }
1096
1097 if (!entry->is_dir && entry->compressed_filesize && SUCCESS != php_stream_copy_to_stream_ex(p->old, p->filefp, entry->compressed_filesize, NULL)) {
1098 spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1099 return ZEND_HASH_APPLY_STOP;
1100 }
1101 }
1102
1103 entry->fp = NULL;
1104 entry->offset = entry->offset_abs = offset;
1105 entry->fp_type = PHAR_FP;
1106
1107 if (entry->metadata_tracker.str) {
1108 if (ZSTR_LEN(entry->metadata_tracker.str) != php_stream_write(p->centralfp, ZSTR_VAL(entry->metadata_tracker.str), ZSTR_LEN(entry->metadata_tracker.str))) {
1109 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);
1110 return ZEND_HASH_APPLY_STOP;
1111 }
1112 }
1113
1114 return ZEND_HASH_APPLY_KEEP;
1115 }
1116 /* }}} */
1117
phar_zip_changed_apply(zval * zv,void * arg)1118 static int phar_zip_changed_apply(zval *zv, void *arg) /* {{{ */
1119 {
1120 return phar_zip_changed_apply_int(Z_PTR_P(zv), arg);
1121 }
1122 /* }}} */
1123
phar_zip_applysignature(phar_archive_data * phar,struct _phar_zip_pass * pass)1124 static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pass *pass) /* {{{ */
1125 {
1126 /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
1127 if (!phar->is_data || phar->sig_flags) {
1128 size_t signature_length;
1129 char *signature, sigbuf[8];
1130 phar_entry_info entry = {0};
1131 php_stream *newfile;
1132 zend_off_t tell;
1133
1134 newfile = php_stream_fopen_tmpfile();
1135 if (newfile == NULL) {
1136 spprintf(pass->error, 0, "phar error: unable to create temporary file for the signature file");
1137 return FAILURE;
1138 }
1139 tell = php_stream_tell(pass->filefp);
1140 /* copy the local files, central directory, and the zip comment to generate the hash */
1141 php_stream_seek(pass->filefp, 0, SEEK_SET);
1142 php_stream_copy_to_stream_ex(pass->filefp, newfile, tell, NULL);
1143 tell = php_stream_tell(pass->centralfp);
1144 php_stream_seek(pass->centralfp, 0, SEEK_SET);
1145 php_stream_copy_to_stream_ex(pass->centralfp, newfile, tell, NULL);
1146 if (phar->metadata_tracker.str) {
1147 php_stream_write(newfile, ZSTR_VAL(phar->metadata_tracker.str), ZSTR_LEN(phar->metadata_tracker.str));
1148 }
1149
1150 if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, pass->error)) {
1151 if (pass->error) {
1152 char *save = *(pass->error);
1153 spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", save);
1154 efree(save);
1155 }
1156
1157 php_stream_close(newfile);
1158 return FAILURE;
1159 }
1160
1161 entry.filename = ".phar/signature.bin";
1162 entry.filename_len = sizeof(".phar/signature.bin")-1;
1163 entry.fp = php_stream_fopen_tmpfile();
1164 entry.fp_type = PHAR_MOD;
1165 entry.is_modified = 1;
1166 if (entry.fp == NULL) {
1167 spprintf(pass->error, 0, "phar error: unable to create temporary file for signature");
1168 return FAILURE;
1169 }
1170
1171 PHAR_SET_32(sigbuf, phar->sig_flags);
1172 PHAR_SET_32(sigbuf + 4, signature_length);
1173
1174 if (Z_UL(8) != php_stream_write(entry.fp, sigbuf, 8) || signature_length != php_stream_write(entry.fp, signature, signature_length)) {
1175 efree(signature);
1176 if (pass->error) {
1177 spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar %s", phar->fname);
1178 }
1179
1180 php_stream_close(newfile);
1181 return FAILURE;
1182 }
1183
1184 efree(signature);
1185 entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
1186 entry.phar = phar;
1187 /* throw out return value and write the signature */
1188 phar_zip_changed_apply_int(&entry, (void *)pass);
1189 php_stream_close(newfile);
1190
1191 if (pass->error && *(pass->error)) {
1192 /* error is set by writeheaders */
1193 return FAILURE;
1194 }
1195 } /* signature */
1196 return SUCCESS;
1197 }
1198 /* }}} */
1199
phar_zip_flush(phar_archive_data * phar,char * user_stub,zend_long len,int defaultstub,char ** error)1200 int phar_zip_flush(phar_archive_data *phar, char *user_stub, zend_long len, int defaultstub, char **error) /* {{{ */
1201 {
1202 char *pos;
1203 static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
1204 char halt_stub[] = "__HALT_COMPILER();";
1205 char *tmp;
1206
1207 php_stream *stubfile, *oldfile;
1208 int free_user_stub, closeoldfile = 0;
1209 phar_entry_info entry = {0};
1210 char *temperr = NULL;
1211 struct _phar_zip_pass pass;
1212 phar_zip_dir_end eocd;
1213 uint32_t cdir_size, cdir_offset;
1214
1215 pass.error = &temperr;
1216 entry.flags = PHAR_ENT_PERM_DEF_FILE;
1217 entry.timestamp = time(NULL);
1218 entry.is_modified = 1;
1219 entry.is_zip = 1;
1220 entry.phar = phar;
1221 entry.fp_type = PHAR_MOD;
1222
1223 if (phar->is_persistent) {
1224 if (error) {
1225 spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
1226 }
1227 return EOF;
1228 }
1229
1230 if (phar->is_data) {
1231 goto nostub;
1232 }
1233
1234 /* set alias */
1235 if (!phar->is_temporary_alias && phar->alias_len) {
1236 entry.fp = php_stream_fopen_tmpfile();
1237 if (entry.fp == NULL) {
1238 spprintf(error, 0, "phar error: unable to create temporary file");
1239 return EOF;
1240 }
1241 if (phar->alias_len != php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
1242 if (error) {
1243 spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1244 }
1245 return EOF;
1246 }
1247
1248 entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len;
1249 entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1250 entry.filename_len = sizeof(".phar/alias.txt")-1;
1251
1252 zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1253 } else {
1254 zend_hash_str_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1255 }
1256
1257 /* register alias */
1258 if (phar->alias_len) {
1259 if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error)) {
1260 return EOF;
1261 }
1262 }
1263
1264 /* set stub */
1265 if (user_stub && !defaultstub) {
1266 if (len < 0) {
1267 /* resource passed in */
1268 if (!(php_stream_from_zval_no_verify(stubfile, (zval *)user_stub))) {
1269 if (error) {
1270 spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1271 }
1272 return EOF;
1273 }
1274
1275 if (len == -1) {
1276 len = PHP_STREAM_COPY_ALL;
1277 } else {
1278 len = -len;
1279 }
1280
1281 user_stub = 0;
1282
1283 // TODO: refactor to avoid reallocation ???
1284 //??? len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)
1285 {
1286 zend_string *str = php_stream_copy_to_mem(stubfile, len, 0);
1287 if (str) {
1288 len = ZSTR_LEN(str);
1289 user_stub = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
1290 zend_string_release_ex(str, 0);
1291 } else {
1292 user_stub = NULL;
1293 len = 0;
1294 }
1295 }
1296
1297 if (!len || !user_stub) {
1298 if (error) {
1299 spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1300 }
1301 return EOF;
1302 }
1303 free_user_stub = 1;
1304 } else {
1305 free_user_stub = 0;
1306 }
1307
1308 tmp = estrndup(user_stub, len);
1309 if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
1310 efree(tmp);
1311 if (error) {
1312 spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
1313 }
1314 if (free_user_stub) {
1315 efree(user_stub);
1316 }
1317 return EOF;
1318 }
1319 pos = user_stub + (pos - tmp);
1320 efree(tmp);
1321
1322 len = pos - user_stub + 18;
1323 entry.fp = php_stream_fopen_tmpfile();
1324 if (entry.fp == NULL) {
1325 spprintf(error, 0, "phar error: unable to create temporary file");
1326 return EOF;
1327 }
1328 entry.uncompressed_filesize = len + 5;
1329
1330 if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
1331 || 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
1332 if (error) {
1333 spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
1334 }
1335 if (free_user_stub) {
1336 efree(user_stub);
1337 }
1338 php_stream_close(entry.fp);
1339 return EOF;
1340 }
1341
1342 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1343 entry.filename_len = sizeof(".phar/stub.php")-1;
1344
1345 zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1346
1347 if (free_user_stub) {
1348 efree(user_stub);
1349 }
1350 } else {
1351 /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1352 entry.fp = php_stream_fopen_tmpfile();
1353 if (entry.fp == NULL) {
1354 spprintf(error, 0, "phar error: unable to create temporary file");
1355 return EOF;
1356 }
1357 if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1358 php_stream_close(entry.fp);
1359 if (error) {
1360 spprintf(error, 0, "unable to %s stub in%szip-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1361 }
1362 return EOF;
1363 }
1364
1365 entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1366 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1367 entry.filename_len = sizeof(".phar/stub.php")-1;
1368
1369 if (!defaultstub) {
1370 if (!zend_hash_str_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1371 if (NULL == zend_hash_str_add_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1372 php_stream_close(entry.fp);
1373 efree(entry.filename);
1374 if (error) {
1375 spprintf(error, 0, "unable to create stub in zip-based phar \"%s\"", phar->fname);
1376 }
1377 return EOF;
1378 }
1379 } else {
1380 php_stream_close(entry.fp);
1381 efree(entry.filename);
1382 }
1383 } else {
1384 zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1385 }
1386 }
1387 nostub:
1388 if (phar->fp && !phar->is_brandnew) {
1389 oldfile = phar->fp;
1390 closeoldfile = 0;
1391 php_stream_rewind(oldfile);
1392 } else {
1393 oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1394 closeoldfile = oldfile != NULL;
1395 }
1396
1397 /* save modified files to the zip */
1398 pass.old = oldfile;
1399 pass.filefp = php_stream_fopen_tmpfile();
1400
1401 if (!pass.filefp) {
1402 fperror:
1403 if (closeoldfile) {
1404 php_stream_close(oldfile);
1405 }
1406 if (error) {
1407 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
1408 }
1409 return EOF;
1410 }
1411
1412 pass.centralfp = php_stream_fopen_tmpfile();
1413
1414 if (!pass.centralfp) {
1415 goto fperror;
1416 }
1417
1418 pass.free_fp = pass.free_ufp = 1;
1419 memset(&eocd, 0, sizeof(eocd));
1420
1421 memcpy(eocd.signature, "PK\5\6", 4);
1422 if (!phar->is_data && !phar->sig_flags) {
1423 phar->sig_flags = PHAR_SIG_SHA256;
1424 }
1425 if (phar->sig_flags) {
1426 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest) + 1);
1427 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest) + 1);
1428 } else {
1429 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
1430 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
1431 }
1432 zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass);
1433
1434 phar_metadata_tracker_try_ensure_has_serialized_data(&phar->metadata_tracker, phar->is_persistent);
1435 if (temperr) {
1436 if (error) {
1437 spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
1438 }
1439 efree(temperr);
1440 temperror:
1441 php_stream_close(pass.centralfp);
1442 nocentralerror:
1443 php_stream_close(pass.filefp);
1444 if (closeoldfile) {
1445 php_stream_close(oldfile);
1446 }
1447 return EOF;
1448 }
1449
1450 if (FAILURE == phar_zip_applysignature(phar, &pass)) {
1451 goto temperror;
1452 }
1453
1454 /* save zip */
1455 cdir_size = php_stream_tell(pass.centralfp);
1456 cdir_offset = php_stream_tell(pass.filefp);
1457 PHAR_SET_32(eocd.cdir_size, cdir_size);
1458 PHAR_SET_32(eocd.cdir_offset, cdir_offset);
1459 php_stream_seek(pass.centralfp, 0, SEEK_SET);
1460
1461 {
1462 size_t clen;
1463 int ret = php_stream_copy_to_stream_ex(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL, &clen);
1464 if (SUCCESS != ret || clen != cdir_size) {
1465 if (error) {
1466 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
1467 }
1468 goto temperror;
1469 }
1470 }
1471
1472 php_stream_close(pass.centralfp);
1473
1474 phar_metadata_tracker_try_ensure_has_serialized_data(&phar->metadata_tracker, phar->is_persistent);
1475 if (phar->metadata_tracker.str) {
1476 /* set phar metadata */
1477 PHAR_SET_16(eocd.comment_len, ZSTR_LEN(phar->metadata_tracker.str));
1478
1479 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1480 if (error) {
1481 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1482 }
1483 goto nocentralerror;
1484 }
1485
1486 if (ZSTR_LEN(phar->metadata_tracker.str) != php_stream_write(pass.filefp, ZSTR_VAL(phar->metadata_tracker.str), ZSTR_LEN(phar->metadata_tracker.str))) {
1487 if (error) {
1488 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
1489 }
1490 goto nocentralerror;
1491 }
1492 } else {
1493 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1494 if (error) {
1495 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1496 }
1497 goto nocentralerror;
1498 }
1499 }
1500
1501 if (phar->fp && pass.free_fp) {
1502 php_stream_close(phar->fp);
1503 }
1504
1505 if (phar->ufp) {
1506 if (pass.free_ufp) {
1507 php_stream_close(phar->ufp);
1508 }
1509 phar->ufp = NULL;
1510 }
1511
1512 /* re-open */
1513 phar->is_brandnew = 0;
1514
1515 if (phar->donotflush) {
1516 /* deferred flush */
1517 phar->fp = pass.filefp;
1518 } else {
1519 phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1520 if (!phar->fp) {
1521 if (closeoldfile) {
1522 php_stream_close(oldfile);
1523 }
1524 phar->fp = pass.filefp;
1525 if (error) {
1526 spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
1527 }
1528 return EOF;
1529 }
1530 php_stream_rewind(pass.filefp);
1531 php_stream_copy_to_stream_ex(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1532 /* we could also reopen the file in "rb" mode but there is no need for that */
1533 php_stream_close(pass.filefp);
1534 }
1535
1536 if (closeoldfile) {
1537 php_stream_close(oldfile);
1538 }
1539 return EOF;
1540 }
1541 /* }}} */
1542