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