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