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