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