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