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