xref: /PHP-8.2/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 			php_stream_tell(fp);
467 			sigfile = php_stream_fopen_tmpfile();
468 			if (!sigfile) {
469 				PHAR_ZIP_FAIL("couldn't open temporary file");
470 			}
471 
472 			php_stream_seek(fp, 0, SEEK_SET);
473 			/* copy file contents + local headers and zip comment, if any, to be hashed for signature */
474 			php_stream_copy_to_stream_ex(fp, sigfile, entry.header_offset, NULL);
475 			/* seek to central directory */
476 			php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
477 			/* copy central directory header */
478 			php_stream_copy_to_stream_ex(fp, sigfile, beforeus - PHAR_GET_32(locator.cdir_offset), NULL);
479 			if (metadata) {
480 				php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len));
481 			}
482 			php_stream_seek(fp, entry.offset, SEEK_SET);
483 			sig = (char *) emalloc(entry.uncompressed_filesize);
484 			read = php_stream_read(fp, sig, entry.uncompressed_filesize);
485 			if (read != entry.uncompressed_filesize || read <= 8) {
486 				php_stream_close(sigfile);
487 				efree(sig);
488 				PHAR_ZIP_FAIL("signature cannot be read");
489 			}
490 			mydata->sig_flags = PHAR_GET_32(sig);
491 			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)) {
492 				efree(sig);
493 				if (error) {
494 					char *save;
495 					php_stream_close(sigfile);
496 					spprintf(&save, 4096, "signature cannot be verified: %s", *error);
497 					efree(*error);
498 					PHAR_ZIP_FAIL_FREE(save, save);
499 				} else {
500 					php_stream_close(sigfile);
501 					PHAR_ZIP_FAIL("signature cannot be verified");
502 				}
503 			}
504 			mydata->sig_len = sig_len;
505 			php_stream_close(sigfile);
506 			efree(sig);
507 			/* signature checked out, let's ensure this is the last file in the phar */
508 			if (i != PHAR_GET_16(locator.count) - 1) {
509 				PHAR_ZIP_FAIL("entries exist after signature, invalid phar");
510 			}
511 
512 			continue;
513 		}
514 
515 		phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len);
516 
517 		if (PHAR_GET_16(zipentry.extra_len)) {
518 			zend_off_t loc = php_stream_tell(fp);
519 			if (FAILURE == phar_zip_process_extra(fp, &entry, PHAR_GET_16(zipentry.extra_len))) {
520 				pefree(entry.filename, entry.is_persistent);
521 				PHAR_ZIP_FAIL("Unable to process extra field header for file in central directory");
522 			}
523 			php_stream_seek(fp, loc + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
524 		}
525 
526 		switch (PHAR_GET_16(zipentry.compressed)) {
527 			case PHAR_ZIP_COMP_NONE :
528 				/* compression flag already set */
529 				break;
530 			case PHAR_ZIP_COMP_DEFLATE :
531 				entry.flags |= PHAR_ENT_COMPRESSED_GZ;
532 				if (!PHAR_G(has_zlib)) {
533 					pefree(entry.filename, entry.is_persistent);
534 					PHAR_ZIP_FAIL("zlib extension is required");
535 				}
536 				break;
537 			case PHAR_ZIP_COMP_BZIP2 :
538 				entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
539 				if (!PHAR_G(has_bz2)) {
540 					pefree(entry.filename, entry.is_persistent);
541 					PHAR_ZIP_FAIL("bzip2 extension is required");
542 				}
543 				break;
544 			case 1 :
545 				pefree(entry.filename, entry.is_persistent);
546 				PHAR_ZIP_FAIL("unsupported compression method (Shrunk) used in this zip");
547 			case 2 :
548 			case 3 :
549 			case 4 :
550 			case 5 :
551 				pefree(entry.filename, entry.is_persistent);
552 				PHAR_ZIP_FAIL("unsupported compression method (Reduce) used in this zip");
553 			case 6 :
554 				pefree(entry.filename, entry.is_persistent);
555 				PHAR_ZIP_FAIL("unsupported compression method (Implode) used in this zip");
556 			case 7 :
557 				pefree(entry.filename, entry.is_persistent);
558 				PHAR_ZIP_FAIL("unsupported compression method (Tokenize) used in this zip");
559 			case 9 :
560 				pefree(entry.filename, entry.is_persistent);
561 				PHAR_ZIP_FAIL("unsupported compression method (Deflate64) used in this zip");
562 			case 10 :
563 				pefree(entry.filename, entry.is_persistent);
564 				PHAR_ZIP_FAIL("unsupported compression method (PKWare Implode/old IBM TERSE) used in this zip");
565 			case 14 :
566 				pefree(entry.filename, entry.is_persistent);
567 				PHAR_ZIP_FAIL("unsupported compression method (LZMA) used in this zip");
568 			case 18 :
569 				pefree(entry.filename, entry.is_persistent);
570 				PHAR_ZIP_FAIL("unsupported compression method (IBM TERSE) used in this zip");
571 			case 19 :
572 				pefree(entry.filename, entry.is_persistent);
573 				PHAR_ZIP_FAIL("unsupported compression method (IBM LZ77) used in this zip");
574 			case 97 :
575 				pefree(entry.filename, entry.is_persistent);
576 				PHAR_ZIP_FAIL("unsupported compression method (WavPack) used in this zip");
577 			case 98 :
578 				pefree(entry.filename, entry.is_persistent);
579 				PHAR_ZIP_FAIL("unsupported compression method (PPMd) used in this zip");
580 			default :
581 				pefree(entry.filename, entry.is_persistent);
582 				PHAR_ZIP_FAIL("unsupported compression method (unknown) used in this zip");
583 		}
584 
585 		/* get file metadata */
586 		if (PHAR_GET_16(zipentry.comment_len)) {
587 			if (PHAR_GET_16(zipentry.comment_len) != php_stream_read(fp, buf, PHAR_GET_16(zipentry.comment_len))) {
588 				pefree(entry.filename, entry.is_persistent);
589 				PHAR_ZIP_FAIL("unable to read in file comment, truncated");
590 			}
591 
592 			p = buf;
593 			phar_parse_metadata_lazy(buf, &entry.metadata_tracker, PHAR_GET_16(zipentry.comment_len), entry.is_persistent);
594 		} else {
595 			ZVAL_UNDEF(&entry.metadata_tracker.val);
596 		}
597 
598 		if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
599 			php_stream_filter *filter;
600 
601 			/* archive alias found */
602 
603 			/* verify local header */
604 			ZEND_ASSERT(!entry.is_dir);
605 			if (entry.crc32 != PHAR_GET_32(local.crc32) || entry.uncompressed_filesize != PHAR_GET_32(local.uncompsize) || entry.compressed_filesize != PHAR_GET_32(local.compsize)) {
606 				pefree(entry.filename, entry.is_persistent);
607 				PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (local header of alias does not match central directory)");
608 			}
609 
610 			zend_off_t restore_pos = php_stream_tell(fp);
611 			php_stream_seek(fp, entry.offset, SEEK_SET);
612 			/* these next lines should be for php < 5.2.6 after 5.3 filters are fixed */
613 			fp->writepos = 0;
614 			fp->readpos = 0;
615 			php_stream_seek(fp, entry.offset, SEEK_SET);
616 			fp->writepos = 0;
617 			fp->readpos = 0;
618 			/* the above lines should be for php < 5.2.6 after 5.3 filters are fixed */
619 
620 			mydata->alias_len = entry.uncompressed_filesize;
621 			if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
622 				filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp));
623 
624 				if (!filter) {
625 					pefree(entry.filename, entry.is_persistent);
626 					PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed");
627 				}
628 
629 				php_stream_filter_append(&fp->readfilters, filter);
630 
631 				// TODO: refactor to avoid reallocation ???
632 //???			entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
633 				{
634 					zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
635 					if (str) {
636 						entry.uncompressed_filesize = ZSTR_LEN(str);
637 						actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
638 						zend_string_release_ex(str, 0);
639 					} else {
640 						actual_alias = NULL;
641 						entry.uncompressed_filesize = 0;
642 					}
643 				}
644 
645 				if (!entry.uncompressed_filesize || !actual_alias) {
646 					pefree(entry.filename, entry.is_persistent);
647 					PHAR_ZIP_FAIL("unable to read in alias, truncated");
648 				}
649 
650 				php_stream_filter_flush(filter, 1);
651 				php_stream_filter_remove(filter, 1);
652 
653 			} else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
654 				filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp));
655 
656 				if (!filter) {
657 					pefree(entry.filename, entry.is_persistent);
658 					PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed");
659 				}
660 
661 				php_stream_filter_append(&fp->readfilters, filter);
662 
663 				// TODO: refactor to avoid reallocation ???
664 //???			entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
665 				{
666 					zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
667 					if (str) {
668 						entry.uncompressed_filesize = ZSTR_LEN(str);
669 						actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
670 						zend_string_release_ex(str, 0);
671 					} else {
672 						actual_alias = NULL;
673 						entry.uncompressed_filesize = 0;
674 					}
675 				}
676 
677 				if (!entry.uncompressed_filesize || !actual_alias) {
678 					pefree(entry.filename, entry.is_persistent);
679 					PHAR_ZIP_FAIL("unable to read in alias, truncated");
680 				}
681 
682 				php_stream_filter_flush(filter, 1);
683 				php_stream_filter_remove(filter, 1);
684 			} else {
685 				// TODO: refactor to avoid reallocation ???
686 //???			entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
687 				{
688 					zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
689 					if (str) {
690 						entry.uncompressed_filesize = ZSTR_LEN(str);
691 						actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
692 						zend_string_release_ex(str, 0);
693 					} else {
694 						actual_alias = NULL;
695 						entry.uncompressed_filesize = 0;
696 					}
697 				}
698 
699 				if (!entry.uncompressed_filesize || !actual_alias) {
700 					pefree(entry.filename, entry.is_persistent);
701 					PHAR_ZIP_FAIL("unable to read in alias, truncated");
702 				}
703 			}
704 
705 			/* return to central directory parsing */
706 			php_stream_seek(fp, restore_pos, SEEK_SET);
707 		}
708 
709 		phar_set_inode(&entry);
710 		zend_hash_str_add_mem(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry, sizeof(phar_entry_info));
711 	}
712 
713 	if (zend_hash_str_exists(&(mydata->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
714 		mydata->is_data = 0;
715 	} else {
716 		mydata->is_data = 1;
717 	}
718 
719 	/* ensure signature set */
720 	if (!mydata->is_data && PHAR_G(require_hash) && !mydata->signature) {
721 		PHAR_ZIP_FAIL("signature is missing");
722 	}
723 
724 	mydata->fp = fp;
725 
726 	zend_hash_str_add_ptr(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len, mydata);
727 
728 	if (actual_alias) {
729 		phar_archive_data *fd_ptr;
730 
731 		if (!phar_validate_alias(actual_alias, mydata->alias_len)) {
732 			if (error) {
733 				spprintf(error, 4096, "phar error: invalid alias \"%s\" in zip-based phar \"%s\"", actual_alias, fname);
734 			}
735 			efree(actual_alias);
736 			zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
737 			return FAILURE;
738 		}
739 
740 		mydata->is_temporary_alias = 0;
741 
742 		if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), actual_alias, mydata->alias_len))) {
743 			if (SUCCESS != phar_free_alias(fd_ptr, actual_alias, mydata->alias_len)) {
744 				if (error) {
745 					spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with implicit alias, alias is already in use", fname);
746 				}
747 				efree(actual_alias);
748 				zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
749 				return FAILURE;
750 			}
751 		}
752 
753 		mydata->alias = entry.is_persistent ? pestrndup(actual_alias, mydata->alias_len, 1) : actual_alias;
754 
755 		if (entry.is_persistent) {
756 			efree(actual_alias);
757 		}
758 
759 		zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), mydata->alias, mydata->alias_len, mydata);
760 	} else {
761 		phar_archive_data *fd_ptr;
762 
763 		if (alias_len) {
764 			if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
765 				if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
766 					if (error) {
767 						spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with explicit alias, alias is already in use", fname);
768 					}
769 					zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
770 					return FAILURE;
771 				}
772 			}
773 
774 			zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, mydata);
775 			mydata->alias = pestrndup(alias, alias_len, mydata->is_persistent);
776 			mydata->alias_len = alias_len;
777 		} else {
778 			mydata->alias = pestrndup(mydata->fname, fname_len, mydata->is_persistent);
779 			mydata->alias_len = fname_len;
780 		}
781 
782 		mydata->is_temporary_alias = 1;
783 	}
784 
785 	if (pphar) {
786 		*pphar = mydata;
787 	}
788 
789 	return SUCCESS;
790 }
791 /* }}} */
792 
793 /**
794  * Create or open a zip-based phar for writing
795  */
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 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) /* {{{ */
797 {
798 	phar_archive_data *phar;
799 	int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error);
800 
801 	if (FAILURE == ret) {
802 		return FAILURE;
803 	}
804 
805 	if (pphar) {
806 		*pphar = phar;
807 	}
808 
809 	phar->is_data = is_data;
810 
811 	if (phar->is_zip) {
812 		return ret;
813 	}
814 
815 	if (phar->is_brandnew) {
816 		phar->internal_file_start = 0;
817 		phar->is_zip = 1;
818 		phar->is_tar = 0;
819 		return SUCCESS;
820 	}
821 
822 	/* we've reached here - the phar exists and is a regular phar */
823 	if (error) {
824 		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);
825 	}
826 
827 	return FAILURE;
828 }
829 /* }}} */
830 
831 struct _phar_zip_pass {
832 	php_stream *filefp;
833 	php_stream *centralfp;
834 	php_stream *old;
835 	int free_fp;
836 	int free_ufp;
837 	char **error;
838 };
839 /* 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)840 static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ */
841 {
842 	phar_zip_file_header local;
843 	phar_zip_unix3 perms;
844 	phar_zip_central_dir_file central;
845 	struct _phar_zip_pass *p;
846 	uint32_t newcrc32;
847 	zend_off_t offset;
848 	int not_really_modified = 0;
849 	p = (struct _phar_zip_pass*) arg;
850 	uint16_t general_purpose_flags;
851 
852 	if (entry->is_mounted) {
853 		return ZEND_HASH_APPLY_KEEP;
854 	}
855 
856 	if (entry->is_deleted) {
857 		if (entry->fp_refcount <= 0) {
858 			return ZEND_HASH_APPLY_REMOVE;
859 		} else {
860 			/* we can't delete this in-memory until it is closed */
861 			return ZEND_HASH_APPLY_KEEP;
862 		}
863 	}
864 
865 	phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len);
866 	memset(&local, 0, sizeof(local));
867 	memset(&central, 0, sizeof(central));
868 	memset(&perms, 0, sizeof(perms));
869 	memcpy(local.signature, "PK\3\4", 4);
870 	memcpy(central.signature, "PK\1\2", 4);
871 	PHAR_SET_16(central.extra_len, sizeof(perms));
872 	PHAR_SET_16(local.extra_len, sizeof(perms));
873 	perms.tag[0] = 'n';
874 	perms.tag[1] = 'u';
875 	PHAR_SET_16(perms.size, sizeof(perms) - 4);
876 	PHAR_SET_16(perms.perms, entry->flags & PHAR_ENT_PERM_MASK);
877 	{
878 		uint32_t crc = (uint32_t) php_crc32_bulk_init();
879 		CRC32(crc, perms.perms[0]);
880 		CRC32(crc, perms.perms[1]);
881 		PHAR_SET_32(perms.crc32, php_crc32_bulk_end(crc));
882 	}
883 
884 	if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
885 		PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_DEFLATE);
886 		PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_DEFLATE);
887 	}
888 
889 	if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
890 		PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_BZIP2);
891 		PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_BZIP2);
892 	}
893 
894 	/* do not use PHAR_GET_16 on either field of the next line */
895 	phar_zip_u2d_time(entry->timestamp, local.timestamp, local.datestamp);
896 	memcpy(central.timestamp, local.timestamp, sizeof(local.timestamp));
897 	memcpy(central.datestamp, local.datestamp, sizeof(local.datestamp));
898 	PHAR_SET_16(central.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
899 	PHAR_SET_16(local.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
900 	// set language encoding flag (all filenames have to be UTF-8 anyway)
901 	general_purpose_flags = PHAR_GET_16(central.flags);
902 	PHAR_SET_16(central.flags, general_purpose_flags | (1 << 11));
903 	general_purpose_flags = PHAR_GET_16(local.flags);
904 	PHAR_SET_16(local.flags, general_purpose_flags | (1 << 11));
905 	PHAR_SET_32(central.offset, php_stream_tell(p->filefp));
906 
907 	/* do extra field for perms later */
908 	if (entry->is_modified) {
909 		php_stream_filter *filter;
910 		php_stream *efp;
911 
912 		if (entry->is_dir) {
913 			entry->is_modified = 0;
914 			if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
915 				php_stream_close(entry->fp);
916 				entry->fp = NULL;
917 				entry->fp_type = PHAR_FP;
918 			}
919 			goto continue_dir;
920 		}
921 
922 		if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
923 			spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
924 			return ZEND_HASH_APPLY_STOP;
925 		}
926 
927 		/* we can be modified and already be compressed, such as when chmod() is executed */
928 		if (entry->flags & PHAR_ENT_COMPRESSION_MASK && (entry->old_flags == entry->flags || !entry->old_flags)) {
929 			not_really_modified = 1;
930 			goto is_compressed;
931 		}
932 
933 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
934 			spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
935 			return ZEND_HASH_APPLY_STOP;
936 		}
937 
938 		efp = phar_get_efp(entry, 0);
939 		newcrc32 = php_crc32_bulk_init();
940 
941 		php_crc32_stream_bulk_update(&newcrc32, efp, entry->uncompressed_filesize);
942 
943 		entry->crc32 = php_crc32_bulk_end(newcrc32);
944 		PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
945 		PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
946 
947 		if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
948 			/* not compressed */
949 			entry->compressed_filesize = entry->uncompressed_filesize;
950 			PHAR_SET_32(central.compsize, entry->uncompressed_filesize);
951 			PHAR_SET_32(local.compsize, entry->uncompressed_filesize);
952 			goto not_compressed;
953 		}
954 
955 		filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
956 
957 		if (!filter) {
958 			if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
959 				spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
960 			} else {
961 				spprintf(p->error, 0, "unable to bzip2 compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
962 			}
963 			return ZEND_HASH_APPLY_STOP;
964 		}
965 
966 		/* create new file that holds the compressed version */
967 		/* work around inability to specify freedom in write and strictness
968 		in read count */
969 		entry->cfp = php_stream_fopen_tmpfile();
970 
971 		if (!entry->cfp) {
972 			spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
973 			return ZEND_HASH_APPLY_STOP;
974 		}
975 
976 		php_stream_flush(efp);
977 
978 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
979 			spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
980 			return ZEND_HASH_APPLY_STOP;
981 		}
982 
983 		php_stream_filter_append((&entry->cfp->writefilters), filter);
984 
985 		if (SUCCESS != php_stream_copy_to_stream_ex(efp, entry->cfp, entry->uncompressed_filesize, NULL)) {
986 			spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname);
987 			return ZEND_HASH_APPLY_STOP;
988 		}
989 
990 		php_stream_filter_flush(filter, 1);
991 		php_stream_flush(entry->cfp);
992 		php_stream_filter_remove(filter, 1);
993 		php_stream_seek(entry->cfp, 0, SEEK_END);
994 		entry->compressed_filesize = (uint32_t) php_stream_tell(entry->cfp);
995 		PHAR_SET_32(central.compsize, entry->compressed_filesize);
996 		PHAR_SET_32(local.compsize, entry->compressed_filesize);
997 		/* generate crc on compressed file */
998 		php_stream_rewind(entry->cfp);
999 		entry->old_flags = entry->flags;
1000 		entry->is_modified = 1;
1001 	} else {
1002 is_compressed:
1003 		PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
1004 		PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
1005 		PHAR_SET_32(central.compsize, entry->compressed_filesize);
1006 		PHAR_SET_32(local.compsize, entry->compressed_filesize);
1007 		if (p->old) {
1008 			if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) {
1009 				spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1010 				return ZEND_HASH_APPLY_STOP;
1011 			}
1012 		}
1013 	}
1014 not_compressed:
1015 	PHAR_SET_32(central.crc32, entry->crc32);
1016 	PHAR_SET_32(local.crc32, entry->crc32);
1017 continue_dir:
1018 	/* set file metadata */
1019 	if (phar_metadata_tracker_has_data(&entry->metadata_tracker, entry->is_persistent)) {
1020 		phar_metadata_tracker_try_ensure_has_serialized_data(&entry->metadata_tracker, entry->is_persistent);
1021 		PHAR_SET_16(central.comment_len, entry->metadata_tracker.str ? ZSTR_LEN(entry->metadata_tracker.str) : 0);
1022 	}
1023 
1024 	entry->header_offset = php_stream_tell(p->filefp);
1025 	offset = entry->header_offset + sizeof(local) + entry->filename_len + (entry->is_dir ? 1 : 0) + sizeof(perms);
1026 
1027 	if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) {
1028 		spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1029 		return ZEND_HASH_APPLY_STOP;
1030 	}
1031 
1032 	if (sizeof(central) != php_stream_write(p->centralfp, (char *)&central, sizeof(central))) {
1033 		spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1034 		return ZEND_HASH_APPLY_STOP;
1035 	}
1036 
1037 	if (entry->is_dir) {
1038 		if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
1039 			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);
1040 			return ZEND_HASH_APPLY_STOP;
1041 		}
1042 
1043 		if (1 != php_stream_write(p->filefp, "/", 1)) {
1044 			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);
1045 			return ZEND_HASH_APPLY_STOP;
1046 		}
1047 
1048 		if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1049 			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);
1050 			return ZEND_HASH_APPLY_STOP;
1051 		}
1052 
1053 		if (1 != php_stream_write(p->centralfp, "/", 1)) {
1054 			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);
1055 			return ZEND_HASH_APPLY_STOP;
1056 		}
1057 	} else {
1058 		if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
1059 			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);
1060 			return ZEND_HASH_APPLY_STOP;
1061 		}
1062 
1063 		if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1064 			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);
1065 			return ZEND_HASH_APPLY_STOP;
1066 		}
1067 	}
1068 
1069 	if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) {
1070 		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);
1071 		return ZEND_HASH_APPLY_STOP;
1072 	}
1073 
1074 	if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) {
1075 		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);
1076 		return ZEND_HASH_APPLY_STOP;
1077 	}
1078 
1079 	if (!not_really_modified && entry->is_modified) {
1080 		if (entry->cfp) {
1081 			if (SUCCESS != php_stream_copy_to_stream_ex(entry->cfp, p->filefp, entry->compressed_filesize, NULL)) {
1082 				spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1083 				return ZEND_HASH_APPLY_STOP;
1084 			}
1085 
1086 			php_stream_close(entry->cfp);
1087 			entry->cfp = NULL;
1088 		} else {
1089 			if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
1090 				return ZEND_HASH_APPLY_STOP;
1091 			}
1092 
1093 			phar_seek_efp(entry, 0, SEEK_SET, 0, 0);
1094 
1095 			if (SUCCESS != php_stream_copy_to_stream_ex(phar_get_efp(entry, 0), p->filefp, entry->uncompressed_filesize, NULL)) {
1096 				spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1097 				return ZEND_HASH_APPLY_STOP;
1098 			}
1099 		}
1100 
1101 		if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) {
1102 			php_stream_close(entry->fp);
1103 		}
1104 
1105 		entry->is_modified = 0;
1106 	} else {
1107 		entry->is_modified = 0;
1108 		if (entry->fp_refcount) {
1109 			/* open file pointers refer to this fp, do not free the stream */
1110 			switch (entry->fp_type) {
1111 				case PHAR_FP:
1112 					p->free_fp = 0;
1113 					break;
1114 				case PHAR_UFP:
1115 					p->free_ufp = 0;
1116 				default:
1117 					break;
1118 			}
1119 		}
1120 
1121 		if (!entry->is_dir && entry->compressed_filesize && SUCCESS != php_stream_copy_to_stream_ex(p->old, p->filefp, entry->compressed_filesize, NULL)) {
1122 			spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1123 			return ZEND_HASH_APPLY_STOP;
1124 		}
1125 	}
1126 
1127 	entry->fp = NULL;
1128 	entry->offset = entry->offset_abs = offset;
1129 	entry->fp_type = PHAR_FP;
1130 
1131 	if (entry->metadata_tracker.str) {
1132 		if (ZSTR_LEN(entry->metadata_tracker.str) != php_stream_write(p->centralfp, ZSTR_VAL(entry->metadata_tracker.str), ZSTR_LEN(entry->metadata_tracker.str))) {
1133 			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);
1134 			return ZEND_HASH_APPLY_STOP;
1135 		}
1136 	}
1137 
1138 	return ZEND_HASH_APPLY_KEEP;
1139 }
1140 /* }}} */
1141 
phar_zip_changed_apply(zval * zv,void * arg)1142 static int phar_zip_changed_apply(zval *zv, void *arg) /* {{{ */
1143 {
1144 	return phar_zip_changed_apply_int(Z_PTR_P(zv), arg);
1145 }
1146 /* }}} */
1147 
phar_zip_applysignature(phar_archive_data * phar,struct _phar_zip_pass * pass)1148 static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pass *pass) /* {{{ */
1149 {
1150 	/* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
1151 	if (!phar->is_data || phar->sig_flags) {
1152 		size_t signature_length;
1153 		char *signature, sigbuf[8];
1154 		phar_entry_info entry = {0};
1155 		php_stream *newfile;
1156 		zend_off_t tell;
1157 
1158 		newfile = php_stream_fopen_tmpfile();
1159 		if (newfile == NULL) {
1160 			spprintf(pass->error, 0, "phar error: unable to create temporary file for the signature file");
1161 			return FAILURE;
1162 		}
1163 		tell = php_stream_tell(pass->filefp);
1164 		/* copy the local files, central directory, and the zip comment to generate the hash */
1165 		php_stream_seek(pass->filefp, 0, SEEK_SET);
1166 		php_stream_copy_to_stream_ex(pass->filefp, newfile, tell, NULL);
1167 		tell = php_stream_tell(pass->centralfp);
1168 		php_stream_seek(pass->centralfp, 0, SEEK_SET);
1169 		php_stream_copy_to_stream_ex(pass->centralfp, newfile, tell, NULL);
1170 		if (phar->metadata_tracker.str) {
1171 			php_stream_write(newfile, ZSTR_VAL(phar->metadata_tracker.str), ZSTR_LEN(phar->metadata_tracker.str));
1172 		}
1173 
1174 		if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, pass->error)) {
1175 			if (pass->error) {
1176 				char *save = *(pass->error);
1177 				spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", save);
1178 				efree(save);
1179 			}
1180 
1181 			php_stream_close(newfile);
1182 			return FAILURE;
1183 		}
1184 
1185 		entry.filename = ".phar/signature.bin";
1186 		entry.filename_len = sizeof(".phar/signature.bin")-1;
1187 		entry.fp = php_stream_fopen_tmpfile();
1188 		entry.fp_type = PHAR_MOD;
1189 		entry.is_modified = 1;
1190 		if (entry.fp == NULL) {
1191 			spprintf(pass->error, 0, "phar error: unable to create temporary file for signature");
1192 			return FAILURE;
1193 		}
1194 
1195 		PHAR_SET_32(sigbuf, phar->sig_flags);
1196 		PHAR_SET_32(sigbuf + 4, signature_length);
1197 
1198 		if (Z_UL(8) != php_stream_write(entry.fp, sigbuf, 8) || signature_length != php_stream_write(entry.fp, signature, signature_length)) {
1199 			efree(signature);
1200 			if (pass->error) {
1201 				spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar %s", phar->fname);
1202 			}
1203 
1204 			php_stream_close(newfile);
1205 			return FAILURE;
1206 		}
1207 
1208 		efree(signature);
1209 		entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
1210 		entry.phar = phar;
1211 		/* throw out return value and write the signature */
1212 		phar_zip_changed_apply_int(&entry, (void *)pass);
1213 		php_stream_close(newfile);
1214 
1215 		if (pass->error && *(pass->error)) {
1216 			/* error is set by writeheaders */
1217 			return FAILURE;
1218 		}
1219 	} /* signature */
1220 	return SUCCESS;
1221 }
1222 /* }}} */
1223 
phar_zip_flush(phar_archive_data * phar,char * user_stub,zend_long len,int defaultstub,char ** error)1224 int phar_zip_flush(phar_archive_data *phar, char *user_stub, zend_long len, int defaultstub, char **error) /* {{{ */
1225 {
1226 	char *pos;
1227 	static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
1228 	char halt_stub[] = "__HALT_COMPILER();";
1229 
1230 	php_stream *stubfile, *oldfile;
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 		if ((pos = php_stristr(user_stub, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
1332 			if (error) {
1333 				spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
1334 			}
1335 			if (free_user_stub) {
1336 				efree(user_stub);
1337 			}
1338 			return EOF;
1339 		}
1340 
1341 		len = pos - user_stub + 18;
1342 		entry.fp = php_stream_fopen_tmpfile();
1343 		if (entry.fp == NULL) {
1344 			spprintf(error, 0, "phar error: unable to create temporary file");
1345 			return EOF;
1346 		}
1347 		entry.uncompressed_filesize = len + 5;
1348 
1349 		if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
1350 		||            5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
1351 			if (error) {
1352 				spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
1353 			}
1354 			if (free_user_stub) {
1355 				efree(user_stub);
1356 			}
1357 			php_stream_close(entry.fp);
1358 			return EOF;
1359 		}
1360 
1361 		entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1362 		entry.filename_len = sizeof(".phar/stub.php")-1;
1363 
1364 		zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1365 
1366 		if (free_user_stub) {
1367 			efree(user_stub);
1368 		}
1369 	} else {
1370 		/* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1371 		entry.fp = php_stream_fopen_tmpfile();
1372 		if (entry.fp == NULL) {
1373 			spprintf(error, 0, "phar error: unable to create temporary file");
1374 			return EOF;
1375 		}
1376 		if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1377 			php_stream_close(entry.fp);
1378 			if (error) {
1379 				spprintf(error, 0, "unable to %s stub in%szip-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1380 			}
1381 			return EOF;
1382 		}
1383 
1384 		entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1385 		entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1386 		entry.filename_len = sizeof(".phar/stub.php")-1;
1387 
1388 		if (!defaultstub) {
1389 			if (!zend_hash_str_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1390 				if (NULL == zend_hash_str_add_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1391 					php_stream_close(entry.fp);
1392 					efree(entry.filename);
1393 					if (error) {
1394 						spprintf(error, 0, "unable to create stub in zip-based phar \"%s\"", phar->fname);
1395 					}
1396 					return EOF;
1397 				}
1398 			} else {
1399 				php_stream_close(entry.fp);
1400 				efree(entry.filename);
1401 			}
1402 		} else {
1403 			zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1404 		}
1405 	}
1406 nostub:
1407 	if (phar->fp && !phar->is_brandnew) {
1408 		oldfile = phar->fp;
1409 		closeoldfile = 0;
1410 		php_stream_rewind(oldfile);
1411 	} else {
1412 		oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1413 		closeoldfile = oldfile != NULL;
1414 	}
1415 
1416 	/* save modified files to the zip */
1417 	pass.old = oldfile;
1418 	pass.filefp = php_stream_fopen_tmpfile();
1419 
1420 	if (!pass.filefp) {
1421 fperror:
1422 		if (closeoldfile) {
1423 			php_stream_close(oldfile);
1424 		}
1425 		if (error) {
1426 			spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
1427 		}
1428 		return EOF;
1429 	}
1430 
1431 	pass.centralfp = php_stream_fopen_tmpfile();
1432 
1433 	if (!pass.centralfp) {
1434 		goto fperror;
1435 	}
1436 
1437 	pass.free_fp = pass.free_ufp = 1;
1438 	memset(&eocd, 0, sizeof(eocd));
1439 
1440 	memcpy(eocd.signature, "PK\5\6", 4);
1441 	if (!phar->is_data && !phar->sig_flags) {
1442 		phar->sig_flags = PHAR_SIG_SHA256;
1443 	}
1444 	if (phar->sig_flags) {
1445 		PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest) + 1);
1446 		PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest) + 1);
1447 	} else {
1448 		PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
1449 		PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
1450 	}
1451 	zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass);
1452 
1453 	phar_metadata_tracker_try_ensure_has_serialized_data(&phar->metadata_tracker, phar->is_persistent);
1454 	if (temperr) {
1455 		if (error) {
1456 			spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
1457 		}
1458 		efree(temperr);
1459 temperror:
1460 		php_stream_close(pass.centralfp);
1461 nocentralerror:
1462 		php_stream_close(pass.filefp);
1463 		if (closeoldfile) {
1464 			php_stream_close(oldfile);
1465 		}
1466 		return EOF;
1467 	}
1468 
1469 	if (FAILURE == phar_zip_applysignature(phar, &pass)) {
1470 		goto temperror;
1471 	}
1472 
1473 	/* save zip */
1474 	cdir_size = php_stream_tell(pass.centralfp);
1475 	cdir_offset = php_stream_tell(pass.filefp);
1476 	PHAR_SET_32(eocd.cdir_size, cdir_size);
1477 	PHAR_SET_32(eocd.cdir_offset, cdir_offset);
1478 	php_stream_seek(pass.centralfp, 0, SEEK_SET);
1479 
1480 	{
1481 		size_t clen;
1482 		int ret = php_stream_copy_to_stream_ex(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL, &clen);
1483 		if (SUCCESS != ret || clen != cdir_size) {
1484 			if (error) {
1485 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
1486 			}
1487 			goto temperror;
1488 		}
1489 	}
1490 
1491 	php_stream_close(pass.centralfp);
1492 
1493 	phar_metadata_tracker_try_ensure_has_serialized_data(&phar->metadata_tracker, phar->is_persistent);
1494 	if (phar->metadata_tracker.str) {
1495 		/* set phar metadata */
1496 		PHAR_SET_16(eocd.comment_len, ZSTR_LEN(phar->metadata_tracker.str));
1497 
1498 		if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1499 			if (error) {
1500 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1501 			}
1502 			goto nocentralerror;
1503 		}
1504 
1505 		if (ZSTR_LEN(phar->metadata_tracker.str) != php_stream_write(pass.filefp, ZSTR_VAL(phar->metadata_tracker.str), ZSTR_LEN(phar->metadata_tracker.str))) {
1506 			if (error) {
1507 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
1508 			}
1509 			goto nocentralerror;
1510 		}
1511 	} else {
1512 		if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1513 			if (error) {
1514 				spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1515 			}
1516 			goto nocentralerror;
1517 		}
1518 	}
1519 
1520 	if (phar->fp && pass.free_fp) {
1521 		php_stream_close(phar->fp);
1522 	}
1523 
1524 	if (phar->ufp) {
1525 		if (pass.free_ufp) {
1526 			php_stream_close(phar->ufp);
1527 		}
1528 		phar->ufp = NULL;
1529 	}
1530 
1531 	/* re-open */
1532 	phar->is_brandnew = 0;
1533 
1534 	if (phar->donotflush) {
1535 		/* deferred flush */
1536 		phar->fp = pass.filefp;
1537 	} else {
1538 		phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1539 		if (!phar->fp) {
1540 			if (closeoldfile) {
1541 				php_stream_close(oldfile);
1542 			}
1543 			phar->fp = pass.filefp;
1544 			if (error) {
1545 				spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
1546 			}
1547 			return EOF;
1548 		}
1549 		php_stream_rewind(pass.filefp);
1550 		php_stream_copy_to_stream_ex(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1551 		/* we could also reopen the file in "rb" mode but there is no need for that */
1552 		php_stream_close(pass.filefp);
1553 	}
1554 
1555 	if (closeoldfile) {
1556 		php_stream_close(oldfile);
1557 	}
1558 	return EOF;
1559 }
1560 /* }}} */
1561