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