xref: /PHP-7.3/ext/phar/phar.c (revision 46561dab)
1 /*
2   +----------------------------------------------------------------------+
3   | phar php single-file executable PHP extension                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2005-2018 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   |          Marcus Boerger <helly@php.net>                              |
17   +----------------------------------------------------------------------+
18 */
19 
20 #define PHAR_MAIN 1
21 #include "phar_internal.h"
22 #include "SAPI.h"
23 #include "func_interceptors.h"
24 
25 static void destroy_phar_data(zval *zv);
26 
27 ZEND_DECLARE_MODULE_GLOBALS(phar)
28 zend_string *(*phar_save_resolve_path)(const char *filename, size_t filename_len);
29 
30 /**
31  * set's phar->is_writeable based on the current INI value
32  */
phar_set_writeable_bit(zval * zv,void * argument)33 static int phar_set_writeable_bit(zval *zv, void *argument) /* {{{ */
34 {
35 	zend_bool keep = *(zend_bool *)argument;
36 	phar_archive_data *phar = (phar_archive_data *)Z_PTR_P(zv);
37 
38 	if (!phar->is_data) {
39 		phar->is_writeable = !keep;
40 	}
41 
42 	return ZEND_HASH_APPLY_KEEP;
43 }
44 /* }}} */
45 
46 /* if the original value is 0 (disabled), then allow setting/unsetting at will. Otherwise only allow 1 (enabled), and error on disabling */
ZEND_INI_MH(phar_ini_modify_handler)47 ZEND_INI_MH(phar_ini_modify_handler) /* {{{ */
48 {
49 	zend_bool old, ini;
50 
51 	if (ZSTR_LEN(entry->name) == sizeof("phar.readonly")-1) {
52 		old = PHAR_G(readonly_orig);
53 	} else {
54 		old = PHAR_G(require_hash_orig);
55 	}
56 
57 	if (ZSTR_LEN(new_value) == 2 && !strcasecmp("on", ZSTR_VAL(new_value))) {
58 		ini = (zend_bool) 1;
59 	}
60 	else if (ZSTR_LEN(new_value) == 3 && !strcasecmp("yes", ZSTR_VAL(new_value))) {
61 		ini = (zend_bool) 1;
62 	}
63 	else if (ZSTR_LEN(new_value) == 4 && !strcasecmp("true", ZSTR_VAL(new_value))) {
64 		ini = (zend_bool) 1;
65 	}
66 	else {
67 		ini = (zend_bool) atoi(ZSTR_VAL(new_value));
68 	}
69 
70 	/* do not allow unsetting in runtime */
71 	if (stage == ZEND_INI_STAGE_STARTUP) {
72 		if (ZSTR_LEN(entry->name) == sizeof("phar.readonly")-1) {
73 			PHAR_G(readonly_orig) = ini;
74 		} else {
75 			PHAR_G(require_hash_orig) = ini;
76 		}
77 	} else if (old && !ini) {
78 		return FAILURE;
79 	}
80 
81 	if (ZSTR_LEN(entry->name) == sizeof("phar.readonly")-1) {
82 		PHAR_G(readonly) = ini;
83 		if (PHAR_G(request_init) && HT_FLAGS(&PHAR_G(phar_fname_map))) {
84 			zend_hash_apply_with_argument(&(PHAR_G(phar_fname_map)), phar_set_writeable_bit, (void *)&ini);
85 		}
86 	} else {
87 		PHAR_G(require_hash) = ini;
88 	}
89 
90 	return SUCCESS;
91 }
92 /* }}}*/
93 
94 /* this global stores the global cached pre-parsed manifests */
95 HashTable cached_phars;
96 HashTable cached_alias;
97 
phar_split_cache_list(void)98 static void phar_split_cache_list(void) /* {{{ */
99 {
100 	char *tmp;
101 	char *key, *lasts, *end;
102 	char ds[2];
103 	phar_archive_data *phar;
104 	uint32_t i = 0;
105 
106 	if (!PHAR_G(cache_list) || !(PHAR_G(cache_list)[0])) {
107 		return;
108 	}
109 
110 	ds[0] = DEFAULT_DIR_SEPARATOR;
111 	ds[1] = '\0';
112 	tmp = estrdup(PHAR_G(cache_list));
113 
114 	/* fake request startup */
115 	PHAR_G(request_init) = 1;
116 	zend_hash_init(&EG(regular_list), 0, NULL, NULL, 0);
117 	EG(regular_list).nNextFreeElement=1;	/* we don't want resource id 0 */
118 
119 	PHAR_G(has_bz2) = zend_hash_str_exists(&module_registry, "bz2", sizeof("bz2")-1);
120 	PHAR_G(has_zlib) = zend_hash_str_exists(&module_registry, "zlib", sizeof("zlib")-1);
121 	/* these two are dummies and will be destroyed later */
122 	zend_hash_init(&cached_phars, sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data,  1);
123 	zend_hash_init(&cached_alias, sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
124 	/* these two are real and will be copied over cached_phars/cached_alias later */
125 	zend_hash_init(&(PHAR_G(phar_fname_map)), sizeof(phar_archive_data*), zend_get_hash_value, destroy_phar_data,  1);
126 	zend_hash_init(&(PHAR_G(phar_alias_map)), sizeof(phar_archive_data*), zend_get_hash_value, NULL, 1);
127 	PHAR_G(manifest_cached) = 1;
128 	PHAR_G(persist) = 1;
129 
130 	for (key = php_strtok_r(tmp, ds, &lasts);
131 			key;
132 			key = php_strtok_r(NULL, ds, &lasts)) {
133 		end = strchr(key, DEFAULT_DIR_SEPARATOR);
134 
135 		if (end) {
136 			if (SUCCESS == phar_open_from_filename(key, end - key, NULL, 0, 0, &phar, NULL)) {
137 finish_up:
138 				phar->phar_pos = i++;
139 				php_stream_close(phar->fp);
140 				phar->fp = NULL;
141 			} else {
142 finish_error:
143 				PHAR_G(persist) = 0;
144 				PHAR_G(manifest_cached) = 0;
145 				efree(tmp);
146 				zend_hash_destroy(&(PHAR_G(phar_fname_map)));
147 				HT_FLAGS(&PHAR_G(phar_fname_map)) = 0;
148 				zend_hash_destroy(&(PHAR_G(phar_alias_map)));
149 				HT_FLAGS(&PHAR_G(phar_alias_map)) = 0;
150 				zend_hash_destroy(&cached_phars);
151 				zend_hash_destroy(&cached_alias);
152 				zend_hash_graceful_reverse_destroy(&EG(regular_list));
153 				memset(&EG(regular_list), 0, sizeof(HashTable));
154 				/* free cached manifests */
155 				PHAR_G(request_init) = 0;
156 				return;
157 			}
158 		} else {
159 			if (SUCCESS == phar_open_from_filename(key, strlen(key), NULL, 0, 0, &phar, NULL)) {
160 				goto finish_up;
161 			} else {
162 				goto finish_error;
163 			}
164 		}
165 	}
166 
167 	PHAR_G(persist) = 0;
168 	PHAR_G(request_init) = 0;
169 	/* destroy dummy values from before */
170 	zend_hash_destroy(&cached_phars);
171 	zend_hash_destroy(&cached_alias);
172 	cached_phars = PHAR_G(phar_fname_map);
173 	cached_alias = PHAR_G(phar_alias_map);
174 	HT_FLAGS(&PHAR_G(phar_fname_map)) = 0;
175 	HT_FLAGS(&PHAR_G(phar_alias_map)) = 0;
176 	zend_hash_graceful_reverse_destroy(&EG(regular_list));
177 	memset(&EG(regular_list), 0, sizeof(HashTable));
178 	efree(tmp);
179 }
180 /* }}} */
181 
ZEND_INI_MH(phar_ini_cache_list)182 ZEND_INI_MH(phar_ini_cache_list) /* {{{ */
183 {
184 	PHAR_G(cache_list) = ZSTR_VAL(new_value);
185 
186 	if (stage == ZEND_INI_STAGE_STARTUP) {
187 		phar_split_cache_list();
188 	}
189 
190 	return SUCCESS;
191 }
192 /* }}} */
193 
194 PHP_INI_BEGIN()
195 	STD_PHP_INI_BOOLEAN("phar.readonly", "1", PHP_INI_ALL, phar_ini_modify_handler, readonly, zend_phar_globals, phar_globals)
196 	STD_PHP_INI_BOOLEAN("phar.require_hash", "1", PHP_INI_ALL, phar_ini_modify_handler, require_hash, zend_phar_globals, phar_globals)
197 	STD_PHP_INI_ENTRY("phar.cache_list", "", PHP_INI_SYSTEM, phar_ini_cache_list, cache_list, zend_phar_globals, phar_globals)
PHP_INI_END()198 PHP_INI_END()
199 
200 /**
201  * When all uses of a phar have been concluded, this frees the manifest
202  * and the phar slot
203  */
204 void phar_destroy_phar_data(phar_archive_data *phar) /* {{{ */
205 {
206 	if (phar->alias && phar->alias != phar->fname) {
207 		pefree(phar->alias, phar->is_persistent);
208 		phar->alias = NULL;
209 	}
210 
211 	if (phar->fname) {
212 		pefree(phar->fname, phar->is_persistent);
213 		phar->fname = NULL;
214 	}
215 
216 	if (phar->signature) {
217 		pefree(phar->signature, phar->is_persistent);
218 		phar->signature = NULL;
219 	}
220 
221 	if (HT_FLAGS(&phar->manifest)) {
222 		zend_hash_destroy(&phar->manifest);
223 		HT_FLAGS(&phar->manifest) = 0;
224 	}
225 
226 	if (HT_FLAGS(&phar->mounted_dirs)) {
227 		zend_hash_destroy(&phar->mounted_dirs);
228 		HT_FLAGS(&phar->mounted_dirs) = 0;
229 	}
230 
231 	if (HT_FLAGS(&phar->virtual_dirs)) {
232 		zend_hash_destroy(&phar->virtual_dirs);
233 		HT_FLAGS(&phar->virtual_dirs) = 0;
234 	}
235 
236 	if (Z_TYPE(phar->metadata) != IS_UNDEF) {
237 		if (phar->is_persistent) {
238 			if (phar->metadata_len) {
239 				/* for zip comments that are strings */
240 				free(Z_PTR(phar->metadata));
241 			} else {
242 				zval_internal_ptr_dtor(&phar->metadata);
243 			}
244 		} else {
245 			zval_ptr_dtor(&phar->metadata);
246 		}
247 		phar->metadata_len = 0;
248 		ZVAL_UNDEF(&phar->metadata);
249 	}
250 
251 	if (phar->fp) {
252 		php_stream_close(phar->fp);
253 		phar->fp = 0;
254 	}
255 
256 	if (phar->ufp) {
257 		php_stream_close(phar->ufp);
258 		phar->ufp = 0;
259 	}
260 
261 	pefree(phar, phar->is_persistent);
262 }
263 /* }}}*/
264 
265 /**
266  * Delete refcount and destruct if needed. On destruct return 1 else 0.
267  */
phar_archive_delref(phar_archive_data * phar)268 int phar_archive_delref(phar_archive_data *phar) /* {{{ */
269 {
270 	if (phar->is_persistent) {
271 		return 0;
272 	}
273 
274 	if (--phar->refcount < 0) {
275 		if (PHAR_G(request_done)
276 		|| zend_hash_str_del(&(PHAR_G(phar_fname_map)), phar->fname, phar->fname_len) != SUCCESS) {
277 			phar_destroy_phar_data(phar);
278 		}
279 		return 1;
280 	} else if (!phar->refcount) {
281 		/* invalidate phar cache */
282 		PHAR_G(last_phar) = NULL;
283 		PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
284 
285 		if (phar->fp && (!(phar->flags & PHAR_FILE_COMPRESSION_MASK) || !phar->alias)) {
286 			/* close open file handle - allows removal or rename of
287 			the file on windows, which has greedy locking
288 			only close if the archive was not already compressed.  If it
289 			was compressed, then the fp does not refer to the original file.
290 			We're also closing compressed files to save resources,
291 			but only if the archive isn't aliased. */
292 			php_stream_close(phar->fp);
293 			phar->fp = NULL;
294 		}
295 
296 		if (!zend_hash_num_elements(&phar->manifest)) {
297 			/* this is a new phar that has perhaps had an alias/metadata set, but has never
298 			been flushed */
299 			if (zend_hash_str_del(&(PHAR_G(phar_fname_map)), phar->fname, phar->fname_len) != SUCCESS) {
300 				phar_destroy_phar_data(phar);
301 			}
302 			return 1;
303 		}
304 	}
305 	return 0;
306 }
307 /* }}}*/
308 
309 /**
310  * Destroy phar's in shutdown, here we don't care about aliases
311  */
destroy_phar_data_only(zval * zv)312 static void destroy_phar_data_only(zval *zv) /* {{{ */
313 {
314 	phar_archive_data *phar_data = (phar_archive_data *) Z_PTR_P(zv);
315 
316 	if (EG(exception) || --phar_data->refcount < 0) {
317 		phar_destroy_phar_data(phar_data);
318 	}
319 }
320 /* }}}*/
321 
322 /**
323  * Delete aliases to phar's that got kicked out of the global table
324  */
phar_unalias_apply(zval * zv,void * argument)325 static int phar_unalias_apply(zval *zv, void *argument) /* {{{ */
326 {
327 	return Z_PTR_P(zv) == argument ? ZEND_HASH_APPLY_REMOVE : ZEND_HASH_APPLY_KEEP;
328 }
329 /* }}} */
330 
331 /**
332  * Delete aliases to phar's that got kicked out of the global table
333  */
phar_tmpclose_apply(zval * zv)334 static int phar_tmpclose_apply(zval *zv) /* {{{ */
335 {
336 	phar_entry_info *entry = (phar_entry_info *) Z_PTR_P(zv);
337 
338 	if (entry->fp_type != PHAR_TMP) {
339 		return ZEND_HASH_APPLY_KEEP;
340 	}
341 
342 	if (entry->fp && !entry->fp_refcount) {
343 		php_stream_close(entry->fp);
344 		entry->fp = NULL;
345 	}
346 
347 	return ZEND_HASH_APPLY_KEEP;
348 }
349 /* }}} */
350 
351 /**
352  * Filename map destructor
353  */
destroy_phar_data(zval * zv)354 static void destroy_phar_data(zval *zv) /* {{{ */
355 {
356 	phar_archive_data *phar_data = (phar_archive_data *)Z_PTR_P(zv);
357 
358 	if (PHAR_G(request_ends)) {
359 		/* first, iterate over the manifest and close all PHAR_TMP entry fp handles,
360 		this prevents unnecessary unfreed stream resources */
361 		zend_hash_apply(&(phar_data->manifest), phar_tmpclose_apply);
362 		destroy_phar_data_only(zv);
363 		return;
364 	}
365 
366 	zend_hash_apply_with_argument(&(PHAR_G(phar_alias_map)), phar_unalias_apply, phar_data);
367 
368 	if (--phar_data->refcount < 0) {
369 		phar_destroy_phar_data(phar_data);
370 	}
371 }
372 /* }}}*/
373 
374 /**
375  * destructor for the manifest hash, frees each file's entry
376  */
destroy_phar_manifest_entry_int(phar_entry_info * entry)377 void destroy_phar_manifest_entry_int(phar_entry_info *entry) /* {{{ */
378 {
379 
380 	if (entry->cfp) {
381 		php_stream_close(entry->cfp);
382 		entry->cfp = 0;
383 	}
384 
385 	if (entry->fp) {
386 		php_stream_close(entry->fp);
387 		entry->fp = 0;
388 	}
389 
390 	if (Z_TYPE(entry->metadata) != IS_UNDEF) {
391 		if (entry->is_persistent) {
392 			if (entry->metadata_len) {
393 				/* for zip comments that are strings */
394 				free(Z_PTR(entry->metadata));
395 			} else {
396 				zval_internal_ptr_dtor(&entry->metadata);
397 			}
398 		} else {
399 			zval_ptr_dtor(&entry->metadata);
400 		}
401 		entry->metadata_len = 0;
402 		ZVAL_UNDEF(&entry->metadata);
403 	}
404 
405 	if (entry->metadata_str.s) {
406 		smart_str_free(&entry->metadata_str);
407 		entry->metadata_str.s = NULL;
408 	}
409 
410 	pefree(entry->filename, entry->is_persistent);
411 
412 	if (entry->link) {
413 		pefree(entry->link, entry->is_persistent);
414 		entry->link = 0;
415 	}
416 
417 	if (entry->tmp) {
418 		pefree(entry->tmp, entry->is_persistent);
419 		entry->tmp = 0;
420 	}
421 }
422 /* }}} */
423 
destroy_phar_manifest_entry(zval * zv)424 void destroy_phar_manifest_entry(zval *zv) /* {{{ */
425 {
426 	phar_entry_info *entry = Z_PTR_P(zv);
427 	destroy_phar_manifest_entry_int(entry);
428 	pefree(entry, entry->is_persistent);
429 }
430 /* }}} */
431 
phar_entry_delref(phar_entry_data * idata)432 int phar_entry_delref(phar_entry_data *idata) /* {{{ */
433 {
434 	int ret = 0;
435 
436 	if (idata->internal_file && !idata->internal_file->is_persistent) {
437 		if (--idata->internal_file->fp_refcount < 0) {
438 			idata->internal_file->fp_refcount = 0;
439 		}
440 
441 		if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
442 			php_stream_close(idata->fp);
443 		}
444 		/* if phar_get_or_create_entry_data returns a sub-directory, we have to free it */
445 		if (idata->internal_file->is_temp_dir) {
446 			destroy_phar_manifest_entry_int(idata->internal_file);
447 			efree(idata->internal_file);
448 		}
449 	}
450 
451 	phar_archive_delref(idata->phar);
452 	efree(idata);
453 	return ret;
454 }
455 /* }}} */
456 
457 /**
458  * Removes an entry, either by actually removing it or by marking it.
459  */
phar_entry_remove(phar_entry_data * idata,char ** error)460 void phar_entry_remove(phar_entry_data *idata, char **error) /* {{{ */
461 {
462 	phar_archive_data *phar;
463 
464 	phar = idata->phar;
465 
466 	if (idata->internal_file->fp_refcount < 2) {
467 		if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
468 			php_stream_close(idata->fp);
469 		}
470 		zend_hash_str_del(&idata->phar->manifest, idata->internal_file->filename, idata->internal_file->filename_len);
471 		idata->phar->refcount--;
472 		efree(idata);
473 	} else {
474 		idata->internal_file->is_deleted = 1;
475 		phar_entry_delref(idata);
476 	}
477 
478 	if (!phar->donotflush) {
479 		phar_flush(phar, 0, 0, 0, error);
480 	}
481 }
482 /* }}} */
483 
484 #define MAPPHAR_ALLOC_FAIL(msg) \
485 	if (fp) {\
486 		php_stream_close(fp);\
487 	}\
488 	if (error) {\
489 		spprintf(error, 0, msg, fname);\
490 	}\
491 	return FAILURE;
492 
493 #define MAPPHAR_FAIL(msg) \
494 	efree(savebuf);\
495 	if (mydata) {\
496 		phar_destroy_phar_data(mydata);\
497 	}\
498 	if (signature) {\
499 		pefree(signature, PHAR_G(persist));\
500 	}\
501 	MAPPHAR_ALLOC_FAIL(msg)
502 
503 #ifdef WORDS_BIGENDIAN
504 # define PHAR_GET_32(buffer, var) \
505 	var = ((((unsigned char*)(buffer))[3]) << 24) \
506 		| ((((unsigned char*)(buffer))[2]) << 16) \
507 		| ((((unsigned char*)(buffer))[1]) <<  8) \
508 		| (((unsigned char*)(buffer))[0]); \
509 	(buffer) += 4
510 # define PHAR_GET_16(buffer, var) \
511 	var = ((((unsigned char*)(buffer))[1]) <<  8) \
512 		| (((unsigned char*)(buffer))[0]); \
513 	(buffer) += 2
514 #else
515 # define PHAR_GET_32(buffer, var) \
516 	memcpy(&var, buffer, sizeof(var)); \
517 	buffer += 4
518 # define PHAR_GET_16(buffer, var) \
519 	var = *(uint16_t*)(buffer); \
520 	buffer += 2
521 #endif
522 #define PHAR_ZIP_16(var) ((uint16_t)((((uint16_t)var[0]) & 0xff) | \
523 	(((uint16_t)var[1]) & 0xff) << 8))
524 #define PHAR_ZIP_32(var) ((uint32_t)((((uint32_t)var[0]) & 0xff) | \
525 	(((uint32_t)var[1]) & 0xff) << 8 | \
526 	(((uint32_t)var[2]) & 0xff) << 16 | \
527 	(((uint32_t)var[3]) & 0xff) << 24))
528 
529 /**
530  * Open an already loaded phar
531  */
phar_open_parsed_phar(char * fname,size_t fname_len,char * alias,size_t alias_len,zend_bool is_data,uint32_t options,phar_archive_data ** pphar,char ** error)532 int phar_open_parsed_phar(char *fname, size_t fname_len, char *alias, size_t alias_len, zend_bool is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */
533 {
534 	phar_archive_data *phar;
535 #ifdef PHP_WIN32
536 	char *unixfname;
537 #endif
538 
539 	if (error) {
540 		*error = NULL;
541 	}
542 #ifdef PHP_WIN32
543 	unixfname = estrndup(fname, fname_len);
544 	phar_unixify_path_separators(unixfname, fname_len);
545 
546 	if (SUCCESS == phar_get_archive(&phar, unixfname, fname_len, alias, alias_len, error)
547 		&& ((alias && fname_len == phar->fname_len
548 		&& !strncmp(unixfname, phar->fname, fname_len)) || !alias)
549 	) {
550 		phar_entry_info *stub;
551 		efree(unixfname);
552 #else
553 	if (SUCCESS == phar_get_archive(&phar, fname, fname_len, alias, alias_len, error)
554 		&& ((alias && fname_len == phar->fname_len
555 		&& !strncmp(fname, phar->fname, fname_len)) || !alias)
556 	) {
557 		phar_entry_info *stub;
558 #endif
559 		/* logic above is as follows:
560 		   If an explicit alias was requested, ensure the filename passed in
561 		   matches the phar's filename.
562 		   If no alias was passed in, then it can match either and be valid
563 		 */
564 
565 		if (!is_data) {
566 			/* prevent any ".phar" without a stub getting through */
567 			if (!phar->halt_offset && !phar->is_brandnew && (phar->is_tar || phar->is_zip)) {
568 				if (PHAR_G(readonly) && NULL == (stub = zend_hash_str_find_ptr(&(phar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1))) {
569 					if (error) {
570 						spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
571 					}
572 					return FAILURE;
573 				}
574 			}
575 		}
576 
577 		if (pphar) {
578 			*pphar = phar;
579 		}
580 
581 		return SUCCESS;
582 	} else {
583 #ifdef PHP_WIN32
584 		efree(unixfname);
585 #endif
586 		if (pphar) {
587 			*pphar = NULL;
588 		}
589 
590 		if (phar && error && !(options & REPORT_ERRORS)) {
591 			efree(error);
592 		}
593 
594 		return FAILURE;
595 	}
596 }
597 /* }}}*/
598 
599 /**
600  * Parse out metadata from the manifest for a single file
601  *
602  * Meta-data is in this format:
603  * [len32][data...]
604  *
605  * data is the serialized zval
606  */
607 int phar_parse_metadata(char **buffer, zval *metadata, uint32_t zip_metadata_len) /* {{{ */
608 {
609 	php_unserialize_data_t var_hash;
610 
611 	if (zip_metadata_len) {
612 		const unsigned char *p;
613 		unsigned char *p_buff = (unsigned char *)estrndup(*buffer, zip_metadata_len);
614 		p = p_buff;
615 		ZVAL_NULL(metadata);
616 		PHP_VAR_UNSERIALIZE_INIT(var_hash);
617 
618 		if (!php_var_unserialize(metadata, &p, p + zip_metadata_len, &var_hash)) {
619 			efree(p_buff);
620 			PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
621 			zval_ptr_dtor(metadata);
622 			ZVAL_UNDEF(metadata);
623 			return FAILURE;
624 		}
625 		efree(p_buff);
626 		PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
627 
628 		if (PHAR_G(persist)) {
629 			/* lazy init metadata */
630 			zval_ptr_dtor(metadata);
631 			Z_PTR_P(metadata) = pemalloc(zip_metadata_len, 1);
632 			memcpy(Z_PTR_P(metadata), *buffer, zip_metadata_len);
633 			return SUCCESS;
634 		}
635 	} else {
636 		ZVAL_UNDEF(metadata);
637 	}
638 
639 	return SUCCESS;
640 }
641 /* }}}*/
642 
643 /**
644  * Size of fixed fields in the manifest.
645  * See: http://php.net/manual/en/phar.fileformat.phar.php
646  */
647 #define MANIFEST_FIXED_LEN	18
648 
649 #define SAFE_PHAR_GET_32(buffer, endbuffer, var) \
650 	if (UNEXPECTED(buffer + 4 > endbuffer)) { \
651 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)"); \
652 	} \
653 	PHAR_GET_32(buffer, var);
654 
655 /**
656  * Does not check for a previously opened phar in the cache.
657  *
658  * Parse a new one and add it to the cache, returning either SUCCESS or
659  * FAILURE, and setting pphar to the pointer to the manifest entry
660  *
661  * This is used by phar_open_from_filename to process the manifest, but can be called
662  * directly.
663  */
664 static int phar_parse_pharfile(php_stream *fp, char *fname, size_t fname_len, char *alias, size_t alias_len, zend_long halt_offset, phar_archive_data** pphar, uint32_t compression, char **error) /* {{{ */
665 {
666 	char b32[4], *buffer, *endbuffer, *savebuf;
667 	phar_archive_data *mydata = NULL;
668 	phar_entry_info entry;
669 	uint32_t manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
670 	uint16_t manifest_ver;
671 	uint32_t len;
672 	zend_long offset;
673 	size_t sig_len;
674 	int register_alias = 0, temp_alias = 0;
675 	char *signature = NULL;
676 	zend_string *str;
677 
678 	if (pphar) {
679 		*pphar = NULL;
680 	}
681 
682 	if (error) {
683 		*error = NULL;
684 	}
685 
686 	/* check for ?>\n and increment accordingly */
687 	if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
688 		MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
689 	}
690 
691 	buffer = b32;
692 
693 	if (3 != php_stream_read(fp, buffer, 3)) {
694 		MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
695 	}
696 
697 	if ((*buffer == ' ' || *buffer == '\n') && *(buffer + 1) == '?' && *(buffer + 2) == '>') {
698 		int nextchar;
699 		halt_offset += 3;
700 		if (EOF == (nextchar = php_stream_getc(fp))) {
701 			MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
702 		}
703 
704 		if ((char) nextchar == '\r') {
705 			/* if we have an \r we require an \n as well */
706 			if (EOF == (nextchar = php_stream_getc(fp)) || (char)nextchar != '\n') {
707 				MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at stub end)")
708 			}
709 			++halt_offset;
710 		}
711 
712 		if ((char) nextchar == '\n') {
713 			++halt_offset;
714 		}
715 	}
716 
717 	/* make sure we are at the right location to read the manifest */
718 	if (-1 == php_stream_seek(fp, halt_offset, SEEK_SET)) {
719 		MAPPHAR_ALLOC_FAIL("cannot seek to __HALT_COMPILER(); location in phar \"%s\"")
720 	}
721 
722 	/* read in manifest */
723 	buffer = b32;
724 
725 	if (4 != php_stream_read(fp, buffer, 4)) {
726 		MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated manifest at manifest length)")
727 	}
728 
729 	PHAR_GET_32(buffer, manifest_len);
730 
731 	if (manifest_len > 1048576 * 100) {
732 		/* prevent serious memory issues by limiting manifest to at most 100 MB in length */
733 		MAPPHAR_ALLOC_FAIL("manifest cannot be larger than 100 MB in phar \"%s\"")
734 	}
735 
736 	buffer = (char *)emalloc(manifest_len);
737 	savebuf = buffer;
738 	endbuffer = buffer + manifest_len;
739 
740 	if (manifest_len < MANIFEST_FIXED_LEN || manifest_len != php_stream_read(fp, buffer, manifest_len)) {
741 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
742 	}
743 
744 	/* extract the number of entries */
745 	SAFE_PHAR_GET_32(buffer, endbuffer, manifest_count);
746 
747 	if (manifest_count == 0) {
748 		MAPPHAR_FAIL("in phar \"%s\", manifest claims to have zero entries.  Phars must have at least 1 entry");
749 	}
750 
751 	/* extract API version, lowest nibble currently unused */
752 	manifest_ver = (((unsigned char)buffer[0]) << 8)
753 				 + ((unsigned char)buffer[1]);
754 	buffer += 2;
755 
756 	if ((manifest_ver & PHAR_API_VER_MASK) < PHAR_API_MIN_READ) {
757 		efree(savebuf);
758 		php_stream_close(fp);
759 		if (error) {
760 			spprintf(error, 0, "phar \"%s\" is API version %1.u.%1.u.%1.u, and cannot be processed", fname, manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0x0F);
761 		}
762 		return FAILURE;
763 	}
764 
765 	SAFE_PHAR_GET_32(buffer, endbuffer, manifest_flags);
766 
767 	manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK;
768 	manifest_flags &= ~PHAR_FILE_COMPRESSION_MASK;
769 	/* remember whether this entire phar was compressed with gz/bzip2 */
770 	manifest_flags |= compression;
771 
772 	/* The lowest nibble contains the phar wide flags. The compression flags can */
773 	/* be ignored on reading because it is being generated anyways. */
774 	if (manifest_flags & PHAR_HDR_SIGNATURE) {
775 		char sig_buf[8], *sig_ptr = sig_buf;
776 		zend_off_t read_len;
777 		size_t end_of_phar;
778 
779 		if (-1 == php_stream_seek(fp, -8, SEEK_END)
780 		|| (read_len = php_stream_tell(fp)) < 20
781 		|| 8 != php_stream_read(fp, sig_buf, 8)
782 		|| memcmp(sig_buf+4, "GBMB", 4)) {
783 			efree(savebuf);
784 			php_stream_close(fp);
785 			if (error) {
786 				spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
787 			}
788 			return FAILURE;
789 		}
790 
791 		PHAR_GET_32(sig_ptr, sig_flags);
792 
793 		switch(sig_flags) {
794 			case PHAR_SIG_OPENSSL: {
795 				uint32_t signature_len;
796 				char *sig;
797 				zend_off_t whence;
798 
799 				/* we store the signature followed by the signature length */
800 				if (-1 == php_stream_seek(fp, -12, SEEK_CUR)
801 				|| 4 != php_stream_read(fp, sig_buf, 4)) {
802 					efree(savebuf);
803 					php_stream_close(fp);
804 					if (error) {
805 						spprintf(error, 0, "phar \"%s\" openssl signature length could not be read", fname);
806 					}
807 					return FAILURE;
808 				}
809 
810 				sig_ptr = sig_buf;
811 				PHAR_GET_32(sig_ptr, signature_len);
812 				sig = (char *) emalloc(signature_len);
813 				whence = signature_len + 4;
814 				whence = -whence;
815 
816 				if (-1 == php_stream_seek(fp, whence, SEEK_CUR)
817 				|| !(end_of_phar = php_stream_tell(fp))
818 				|| signature_len != php_stream_read(fp, sig, signature_len)) {
819 					efree(savebuf);
820 					efree(sig);
821 					php_stream_close(fp);
822 					if (error) {
823 						spprintf(error, 0, "phar \"%s\" openssl signature could not be read", fname);
824 					}
825 					return FAILURE;
826 				}
827 
828 				if (FAILURE == phar_verify_signature(fp, end_of_phar, PHAR_SIG_OPENSSL, sig, signature_len, fname, &signature, &sig_len, error)) {
829 					efree(savebuf);
830 					efree(sig);
831 					php_stream_close(fp);
832 					if (error) {
833 						char *save = *error;
834 						spprintf(error, 0, "phar \"%s\" openssl signature could not be verified: %s", fname, *error);
835 						efree(save);
836 					}
837 					return FAILURE;
838 				}
839 				efree(sig);
840 			}
841 			break;
842 #if PHAR_HASH_OK
843 			case PHAR_SIG_SHA512: {
844 				unsigned char digest[64];
845 
846 				php_stream_seek(fp, -(8 + 64), SEEK_END);
847 				read_len = php_stream_tell(fp);
848 
849 				if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
850 					efree(savebuf);
851 					php_stream_close(fp);
852 					if (error) {
853 						spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
854 					}
855 					return FAILURE;
856 				}
857 
858 				if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA512, (char *)digest, 64, fname, &signature, &sig_len, error)) {
859 					efree(savebuf);
860 					php_stream_close(fp);
861 					if (error) {
862 						char *save = *error;
863 						spprintf(error, 0, "phar \"%s\" SHA512 signature could not be verified: %s", fname, *error);
864 						efree(save);
865 					}
866 					return FAILURE;
867 				}
868 				break;
869 			}
870 			case PHAR_SIG_SHA256: {
871 				unsigned char digest[32];
872 
873 				php_stream_seek(fp, -(8 + 32), SEEK_END);
874 				read_len = php_stream_tell(fp);
875 
876 				if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
877 					efree(savebuf);
878 					php_stream_close(fp);
879 					if (error) {
880 						spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
881 					}
882 					return FAILURE;
883 				}
884 
885 				if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA256, (char *)digest, 32, fname, &signature, &sig_len, error)) {
886 					efree(savebuf);
887 					php_stream_close(fp);
888 					if (error) {
889 						char *save = *error;
890 						spprintf(error, 0, "phar \"%s\" SHA256 signature could not be verified: %s", fname, *error);
891 						efree(save);
892 					}
893 					return FAILURE;
894 				}
895 				break;
896 			}
897 #else
898 			case PHAR_SIG_SHA512:
899 			case PHAR_SIG_SHA256:
900 				efree(savebuf);
901 				php_stream_close(fp);
902 
903 				if (error) {
904 					spprintf(error, 0, "phar \"%s\" has a unsupported signature", fname);
905 				}
906 				return FAILURE;
907 #endif
908 			case PHAR_SIG_SHA1: {
909 				unsigned char digest[20];
910 
911 				php_stream_seek(fp, -(8 + 20), SEEK_END);
912 				read_len = php_stream_tell(fp);
913 
914 				if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
915 					efree(savebuf);
916 					php_stream_close(fp);
917 					if (error) {
918 						spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
919 					}
920 					return FAILURE;
921 				}
922 
923 				if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_SHA1, (char *)digest, 20, fname, &signature, &sig_len, error)) {
924 					efree(savebuf);
925 					php_stream_close(fp);
926 					if (error) {
927 						char *save = *error;
928 						spprintf(error, 0, "phar \"%s\" SHA1 signature could not be verified: %s", fname, *error);
929 						efree(save);
930 					}
931 					return FAILURE;
932 				}
933 				break;
934 			}
935 			case PHAR_SIG_MD5: {
936 				unsigned char digest[16];
937 
938 				php_stream_seek(fp, -(8 + 16), SEEK_END);
939 				read_len = php_stream_tell(fp);
940 
941 				if (php_stream_read(fp, (char*)digest, sizeof(digest)) != sizeof(digest)) {
942 					efree(savebuf);
943 					php_stream_close(fp);
944 					if (error) {
945 						spprintf(error, 0, "phar \"%s\" has a broken signature", fname);
946 					}
947 					return FAILURE;
948 				}
949 
950 				if (FAILURE == phar_verify_signature(fp, read_len, PHAR_SIG_MD5, (char *)digest, 16, fname, &signature, &sig_len, error)) {
951 					efree(savebuf);
952 					php_stream_close(fp);
953 					if (error) {
954 						char *save = *error;
955 						spprintf(error, 0, "phar \"%s\" MD5 signature could not be verified: %s", fname, *error);
956 						efree(save);
957 					}
958 					return FAILURE;
959 				}
960 				break;
961 			}
962 			default:
963 				efree(savebuf);
964 				php_stream_close(fp);
965 
966 				if (error) {
967 					spprintf(error, 0, "phar \"%s\" has a broken or unsupported signature", fname);
968 				}
969 				return FAILURE;
970 		}
971 	} else if (PHAR_G(require_hash)) {
972 		efree(savebuf);
973 		php_stream_close(fp);
974 
975 		if (error) {
976 			spprintf(error, 0, "phar \"%s\" does not have a signature", fname);
977 		}
978 		return FAILURE;
979 	} else {
980 		sig_flags = 0;
981 		sig_len = 0;
982 	}
983 
984 	/* extract alias */
985 	SAFE_PHAR_GET_32(buffer, endbuffer, tmp_len);
986 
987 	if (buffer + tmp_len > endbuffer) {
988 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (buffer overrun)");
989 	}
990 
991 	if (manifest_len < MANIFEST_FIXED_LEN + tmp_len) {
992 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
993 	}
994 
995 	/* tmp_len = 0 says alias length is 0, which means the alias is not stored in the phar */
996 	if (tmp_len) {
997 		/* if the alias is stored we enforce it (implicit overrides explicit) */
998 		if (alias && alias_len && (alias_len != tmp_len || strncmp(alias, buffer, tmp_len)))
999 		{
1000 			php_stream_close(fp);
1001 
1002 			if (signature) {
1003 				efree(signature);
1004 			}
1005 
1006 			if (error) {
1007 				spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%.*s\" under different alias \"%s\"", fname, tmp_len, buffer, alias);
1008 			}
1009 
1010 			efree(savebuf);
1011 			return FAILURE;
1012 		}
1013 
1014 		alias_len = tmp_len;
1015 		alias = buffer;
1016 		buffer += tmp_len;
1017 		register_alias = 1;
1018 	} else if (!alias_len || !alias) {
1019 		/* if we neither have an explicit nor an implicit alias, we use the filename */
1020 		alias = NULL;
1021 		alias_len = 0;
1022 		register_alias = 0;
1023 	} else if (alias_len) {
1024 		register_alias = 1;
1025 		temp_alias = 1;
1026 	}
1027 
1028 	/* we have 5 32-bit items plus 1 byte at least */
1029 	if (manifest_count > ((manifest_len - MANIFEST_FIXED_LEN - tmp_len) / (5 * 4 + 1))) {
1030 		/* prevent serious memory issues */
1031 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)")
1032 	}
1033 
1034 	mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
1035 	mydata->is_persistent = PHAR_G(persist);
1036 
1037 	/* check whether we have meta data, zero check works regardless of byte order */
1038 	SAFE_PHAR_GET_32(buffer, endbuffer, len);
1039 	if (mydata->is_persistent) {
1040 		mydata->metadata_len = len;
1041 		if (!len) {
1042 			/* FIXME: not sure why this is needed but removing it breaks tests */
1043 			SAFE_PHAR_GET_32(buffer, endbuffer, len);
1044 		}
1045 	}
1046 	if(len > (size_t)(endbuffer - buffer)) {
1047 		MAPPHAR_FAIL("internal corruption of phar \"%s\" (trying to read past buffer end)");
1048 	}
1049 	if (phar_parse_metadata(&buffer, &mydata->metadata, len) == FAILURE) {
1050 		MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
1051 	}
1052 	buffer += len;
1053 
1054 	/* set up our manifest */
1055 	zend_hash_init(&mydata->manifest, manifest_count,
1056 		zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
1057 	zend_hash_init(&mydata->mounted_dirs, 5,
1058 		zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1059 	zend_hash_init(&mydata->virtual_dirs, manifest_count * 2,
1060 		zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1061 	mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
1062 #ifdef PHP_WIN32
1063 	phar_unixify_path_separators(mydata->fname, fname_len);
1064 #endif
1065 	mydata->fname_len = fname_len;
1066 	offset = halt_offset + manifest_len + 4;
1067 	memset(&entry, 0, sizeof(phar_entry_info));
1068 	entry.phar = mydata;
1069 	entry.fp_type = PHAR_FP;
1070 	entry.is_persistent = mydata->is_persistent;
1071 
1072 	for (manifest_index = 0; manifest_index < manifest_count; ++manifest_index) {
1073 		if (buffer + 28 > endbuffer) {
1074 			MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)")
1075 		}
1076 
1077 		PHAR_GET_32(buffer, entry.filename_len);
1078 
1079 		if (entry.filename_len == 0) {
1080 			MAPPHAR_FAIL("zero-length filename encountered in phar \"%s\"");
1081 		}
1082 
1083 		if (entry.is_persistent) {
1084 			entry.manifest_pos = manifest_index;
1085 		}
1086 
1087 		if (entry.filename_len > (size_t)(endbuffer - buffer - 24)) {
1088 			MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
1089 		}
1090 
1091 		if ((manifest_ver & PHAR_API_VER_MASK) >= PHAR_API_MIN_DIR && buffer[entry.filename_len - 1] == '/') {
1092 			entry.is_dir = 1;
1093 		} else {
1094 			entry.is_dir = 0;
1095 		}
1096 
1097 		phar_add_virtual_dirs(mydata, buffer, entry.filename_len);
1098 		entry.filename = pestrndup(buffer, entry.filename_len, entry.is_persistent);
1099 		buffer += entry.filename_len;
1100 		PHAR_GET_32(buffer, entry.uncompressed_filesize);
1101 		PHAR_GET_32(buffer, entry.timestamp);
1102 
1103 		if (offset == halt_offset + manifest_len + 4) {
1104 			mydata->min_timestamp = entry.timestamp;
1105 			mydata->max_timestamp = entry.timestamp;
1106 		} else {
1107 			if (mydata->min_timestamp > entry.timestamp) {
1108 				mydata->min_timestamp = entry.timestamp;
1109 			} else if (mydata->max_timestamp < entry.timestamp) {
1110 				mydata->max_timestamp = entry.timestamp;
1111 			}
1112 		}
1113 
1114 		PHAR_GET_32(buffer, entry.compressed_filesize);
1115 		PHAR_GET_32(buffer, entry.crc32);
1116 		PHAR_GET_32(buffer, entry.flags);
1117 
1118 		if (entry.is_dir) {
1119 			entry.filename_len--;
1120 			entry.flags |= PHAR_ENT_PERM_DEF_DIR;
1121 		}
1122 
1123 		PHAR_GET_32(buffer, len);
1124 		if (entry.is_persistent) {
1125 			entry.metadata_len = len;
1126 		} else {
1127 			entry.metadata_len = 0;
1128 		}
1129 		if (len > (size_t)(endbuffer - buffer)) {
1130 			pefree(entry.filename, entry.is_persistent);
1131 			MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)");
1132 		}
1133 		if (phar_parse_metadata(&buffer, &entry.metadata, len) == FAILURE) {
1134 			pefree(entry.filename, entry.is_persistent);
1135 			MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
1136 		}
1137 		buffer += len;
1138 
1139 		entry.offset = entry.offset_abs = offset;
1140 		offset += entry.compressed_filesize;
1141 
1142 		switch (entry.flags & PHAR_ENT_COMPRESSION_MASK) {
1143 			case PHAR_ENT_COMPRESSED_GZ:
1144 				if (!PHAR_G(has_zlib)) {
1145 					if (Z_TYPE(entry.metadata) != IS_UNDEF) {
1146 						if (entry.is_persistent) {
1147 							free(Z_PTR(entry.metadata));
1148 						} else {
1149 							zval_ptr_dtor(&entry.metadata);
1150 						}
1151 					}
1152 					pefree(entry.filename, entry.is_persistent);
1153 					MAPPHAR_FAIL("zlib extension is required for gz compressed .phar file \"%s\"");
1154 				}
1155 				break;
1156 			case PHAR_ENT_COMPRESSED_BZ2:
1157 				if (!PHAR_G(has_bz2)) {
1158 					if (Z_TYPE(entry.metadata) != IS_UNDEF) {
1159 						if (entry.is_persistent) {
1160 							free(Z_PTR(entry.metadata));
1161 						} else {
1162 							zval_ptr_dtor(&entry.metadata);
1163 						}
1164 					}
1165 					pefree(entry.filename, entry.is_persistent);
1166 					MAPPHAR_FAIL("bz2 extension is required for bzip2 compressed .phar file \"%s\"");
1167 				}
1168 				break;
1169 			default:
1170 				if (entry.uncompressed_filesize != entry.compressed_filesize) {
1171 					if (Z_TYPE(entry.metadata) != IS_UNDEF) {
1172 						if (entry.is_persistent) {
1173 							free(Z_PTR(entry.metadata));
1174 						} else {
1175 							zval_ptr_dtor(&entry.metadata);
1176 						}
1177 					}
1178 					pefree(entry.filename, entry.is_persistent);
1179 					MAPPHAR_FAIL("internal corruption of phar \"%s\" (compressed and uncompressed size does not match for uncompressed entry)");
1180 				}
1181 				break;
1182 		}
1183 
1184 		manifest_flags |= (entry.flags & PHAR_ENT_COMPRESSION_MASK);
1185 		/* if signature matched, no need to check CRC32 for each file */
1186 		entry.is_crc_checked = (manifest_flags & PHAR_HDR_SIGNATURE ? 1 : 0);
1187 		phar_set_inode(&entry);
1188 		if (mydata->is_persistent) {
1189 			str = zend_string_init_interned(entry.filename, entry.filename_len, 1);
1190 		} else {
1191 			str = zend_string_init(entry.filename, entry.filename_len, 0);
1192 		}
1193 		zend_hash_add_mem(&mydata->manifest, str, (void*)&entry, sizeof(phar_entry_info));
1194 		zend_string_release(str);
1195 	}
1196 
1197 	snprintf(mydata->version, sizeof(mydata->version), "%u.%u.%u", manifest_ver >> 12, (manifest_ver >> 8) & 0xF, (manifest_ver >> 4) & 0xF);
1198 	mydata->internal_file_start = halt_offset + manifest_len + 4;
1199 	mydata->halt_offset = halt_offset;
1200 	mydata->flags = manifest_flags;
1201 	endbuffer = strrchr(mydata->fname, '/');
1202 
1203 	if (endbuffer) {
1204 		mydata->ext = memchr(endbuffer, '.', (mydata->fname + fname_len) - endbuffer);
1205 		if (mydata->ext == endbuffer) {
1206 			mydata->ext = memchr(endbuffer + 1, '.', (mydata->fname + fname_len) - endbuffer - 1);
1207 		}
1208 		if (mydata->ext) {
1209 			mydata->ext_len = (mydata->fname + mydata->fname_len) - mydata->ext;
1210 		}
1211 	}
1212 
1213 	mydata->alias = alias ?
1214 		pestrndup(alias, alias_len, mydata->is_persistent) :
1215 		pestrndup(mydata->fname, fname_len, mydata->is_persistent);
1216 	mydata->alias_len = alias ? alias_len : fname_len;
1217 	mydata->sig_flags = sig_flags;
1218 	mydata->fp = fp;
1219 	mydata->sig_len = sig_len;
1220 	mydata->signature = signature;
1221 	phar_request_initialize();
1222 
1223 	if (register_alias) {
1224 		phar_archive_data *fd_ptr;
1225 
1226 		mydata->is_temporary_alias = temp_alias;
1227 
1228 		if (!phar_validate_alias(mydata->alias, mydata->alias_len)) {
1229 			signature = NULL;
1230 			fp = NULL;
1231 			MAPPHAR_FAIL("Cannot open archive \"%s\", invalid alias");
1232 		}
1233 
1234 		if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
1235 			if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
1236 				signature = NULL;
1237 				fp = NULL;
1238 				MAPPHAR_FAIL("Cannot open archive \"%s\", alias is already in use by existing archive");
1239 			}
1240 		}
1241 
1242 		if (mydata->is_persistent) {
1243 			str = zend_string_init_interned(alias, alias_len, 1);
1244 		} else {
1245 			str = zend_string_init(alias, alias_len, 0);
1246 		}
1247 		zend_hash_add_ptr(&(PHAR_G(phar_alias_map)), str, mydata);
1248 		zend_string_release(str);
1249 	} else {
1250 		mydata->is_temporary_alias = 1;
1251 	}
1252 
1253 	if (mydata->is_persistent) {
1254 		str = zend_string_init_interned(mydata->fname, fname_len, 1);
1255 	} else {
1256 		str = zend_string_init(mydata->fname, fname_len, 0);
1257 	}
1258 	zend_hash_add_ptr(&(PHAR_G(phar_fname_map)), str, mydata);
1259 	zend_string_release(str);
1260 	efree(savebuf);
1261 
1262 	if (pphar) {
1263 		*pphar = mydata;
1264 	}
1265 
1266 	return SUCCESS;
1267 }
1268 /* }}} */
1269 
1270 /**
1271  * Create or open a phar for writing
1272  */
1273 int phar_open_or_create_filename(char *fname, size_t fname_len, char *alias, size_t alias_len, zend_bool is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */
1274 {
1275 	const char *ext_str, *z;
1276 	char *my_error;
1277 	size_t ext_len;
1278 	phar_archive_data **test, *unused = NULL;
1279 
1280 	test = &unused;
1281 
1282 	if (error) {
1283 		*error = NULL;
1284 	}
1285 
1286 	/* first try to open an existing file */
1287 	if (phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 0, 1) == SUCCESS) {
1288 		goto check_file;
1289 	}
1290 
1291 	/* next try to create a new file */
1292 	if (FAILURE == phar_detect_phar_fname_ext(fname, fname_len, &ext_str, &ext_len, !is_data, 1, 1)) {
1293 		if (error) {
1294 			if (ext_len == -2) {
1295 				spprintf(error, 0, "Cannot create a phar archive from a URL like \"%s\". Phar objects can only be created from local files", fname);
1296 			} else {
1297 				spprintf(error, 0, "Cannot create phar '%s', file extension (or combination) not recognised or the directory does not exist", fname);
1298 			}
1299 		}
1300 		return FAILURE;
1301 	}
1302 check_file:
1303 	if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, test, &my_error) == SUCCESS) {
1304 		if (pphar) {
1305 			*pphar = *test;
1306 		}
1307 
1308 		if ((*test)->is_data && !(*test)->is_tar && !(*test)->is_zip) {
1309 			if (error) {
1310 				spprintf(error, 0, "Cannot open '%s' as a PharData object. Use Phar::__construct() for executable archives", fname);
1311 			}
1312 			return FAILURE;
1313 		}
1314 
1315 		if (PHAR_G(readonly) && !(*test)->is_data && ((*test)->is_tar || (*test)->is_zip)) {
1316 			phar_entry_info *stub;
1317 			if (NULL == (stub = zend_hash_str_find_ptr(&((*test)->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1))) {
1318 				spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname);
1319 				return FAILURE;
1320 			}
1321 		}
1322 
1323 		if (!PHAR_G(readonly) || (*test)->is_data) {
1324 			(*test)->is_writeable = 1;
1325 		}
1326 		return SUCCESS;
1327 	} else if (my_error) {
1328 		if (error) {
1329 			*error = my_error;
1330 		} else {
1331 			efree(my_error);
1332 		}
1333 		return FAILURE;
1334 	}
1335 
1336 	if (ext_len > 3 && (z = memchr(ext_str, 'z', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ip", 2)) {
1337 		/* assume zip-based phar */
1338 		return phar_open_or_create_zip(fname, fname_len, alias, alias_len, is_data, options, pphar, error);
1339 	}
1340 
1341 	if (ext_len > 3 && (z = memchr(ext_str, 't', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ar", 2)) {
1342 		/* assume tar-based phar */
1343 		return phar_open_or_create_tar(fname, fname_len, alias, alias_len, is_data, options, pphar, error);
1344 	}
1345 
1346 	return phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, pphar, error);
1347 }
1348 /* }}} */
1349 
1350 int phar_create_or_parse_filename(char *fname, size_t fname_len, char *alias, size_t alias_len, zend_bool is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */
1351 {
1352 	phar_archive_data *mydata;
1353 	php_stream *fp;
1354 	zend_string *actual = NULL;
1355 	char *p;
1356 
1357 	if (!pphar) {
1358 		pphar = &mydata;
1359 	}
1360 	if (php_check_open_basedir(fname)) {
1361 		return FAILURE;
1362 	}
1363 
1364 	/* first open readonly so it won't be created if not present */
1365 	fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, &actual);
1366 
1367 	if (actual) {
1368 		fname = ZSTR_VAL(actual);
1369 		fname_len = ZSTR_LEN(actual);
1370 	}
1371 
1372 	if (fp) {
1373 		if (phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error) == SUCCESS) {
1374 			if ((*pphar)->is_data || !PHAR_G(readonly)) {
1375 				(*pphar)->is_writeable = 1;
1376 			}
1377 			if (actual) {
1378 				zend_string_release_ex(actual, 0);
1379 			}
1380 			return SUCCESS;
1381 		} else {
1382 			/* file exists, but is either corrupt or not a phar archive */
1383 			if (actual) {
1384 				zend_string_release_ex(actual, 0);
1385 			}
1386 			return FAILURE;
1387 		}
1388 	}
1389 
1390 	if (actual) {
1391 		zend_string_release_ex(actual, 0);
1392 	}
1393 
1394 	if (PHAR_G(readonly) && !is_data) {
1395 		if (options & REPORT_ERRORS) {
1396 			if (error) {
1397 				spprintf(error, 0, "creating archive \"%s\" disabled by the php.ini setting phar.readonly", fname);
1398 			}
1399 		}
1400 		return FAILURE;
1401 	}
1402 
1403 	/* set up our manifest */
1404 	mydata = ecalloc(1, sizeof(phar_archive_data));
1405 	mydata->fname = expand_filepath(fname, NULL);
1406 	if (mydata->fname == NULL) {
1407 		efree(mydata);
1408 		return FAILURE;
1409 	}
1410 	fname_len = strlen(mydata->fname);
1411 #ifdef PHP_WIN32
1412 	phar_unixify_path_separators(mydata->fname, fname_len);
1413 #endif
1414 	p = strrchr(mydata->fname, '/');
1415 
1416 	if (p) {
1417 		mydata->ext = memchr(p, '.', (mydata->fname + fname_len) - p);
1418 		if (mydata->ext == p) {
1419 			mydata->ext = memchr(p + 1, '.', (mydata->fname + fname_len) - p - 1);
1420 		}
1421 		if (mydata->ext) {
1422 			mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
1423 		}
1424 	}
1425 
1426 	if (pphar) {
1427 		*pphar = mydata;
1428 	}
1429 
1430 	zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
1431 		zend_get_hash_value, destroy_phar_manifest_entry, 0);
1432 	zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
1433 		zend_get_hash_value, NULL, 0);
1434 	zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
1435 		zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
1436 	mydata->fname_len = fname_len;
1437 	snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION);
1438 	mydata->is_temporary_alias = alias ? 0 : 1;
1439 	mydata->internal_file_start = -1;
1440 	mydata->fp = NULL;
1441 	mydata->is_writeable = 1;
1442 	mydata->is_brandnew = 1;
1443 	phar_request_initialize();
1444 	zend_hash_str_add_ptr(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len, mydata);
1445 
1446 	if (is_data) {
1447 		alias = NULL;
1448 		alias_len = 0;
1449 		mydata->is_data = 1;
1450 		/* assume tar format, PharData can specify other */
1451 		mydata->is_tar = 1;
1452 	} else {
1453 		phar_archive_data *fd_ptr;
1454 
1455 		if (alias && NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
1456 			if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
1457 				if (error) {
1458 					spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias);
1459 				}
1460 
1461 				zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
1462 
1463 				if (pphar) {
1464 					*pphar = NULL;
1465 				}
1466 
1467 				return FAILURE;
1468 			}
1469 		}
1470 
1471 		mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len);
1472 		mydata->alias_len = alias ? alias_len : fname_len;
1473 	}
1474 
1475 	if (alias_len && alias) {
1476 		if (NULL == zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, mydata)) {
1477 			if (options & REPORT_ERRORS) {
1478 				if (error) {
1479 					spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", fname, alias);
1480 				}
1481 			}
1482 
1483 			zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
1484 
1485 			if (pphar) {
1486 				*pphar = NULL;
1487 			}
1488 
1489 			return FAILURE;
1490 		}
1491 	}
1492 
1493 	return SUCCESS;
1494 }
1495 /* }}}*/
1496 
1497 /**
1498  * Return an already opened filename.
1499  *
1500  * Or scan a phar file for the required __HALT_COMPILER(); ?> token and verify
1501  * that the manifest is proper, then pass it to phar_parse_pharfile().  SUCCESS
1502  * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1503  */
1504 int phar_open_from_filename(char *fname, size_t fname_len, char *alias, size_t alias_len, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */
1505 {
1506 	php_stream *fp;
1507 	zend_string *actual;
1508 	int ret, is_data = 0;
1509 
1510 	if (error) {
1511 		*error = NULL;
1512 	}
1513 
1514 	if (!strstr(fname, ".phar")) {
1515 		is_data = 1;
1516 	}
1517 
1518 	if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, pphar, error) == SUCCESS) {
1519 		return SUCCESS;
1520 	} else if (error && *error) {
1521 		return FAILURE;
1522 	}
1523 	if (php_check_open_basedir(fname)) {
1524 		return FAILURE;
1525 	}
1526 
1527 	fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual);
1528 
1529 	if (!fp) {
1530 		if (options & REPORT_ERRORS) {
1531 			if (error) {
1532 				spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
1533 			}
1534 		}
1535 		if (actual) {
1536 			zend_string_release_ex(actual, 0);
1537 		}
1538 		return FAILURE;
1539 	}
1540 
1541 	if (actual) {
1542 		fname = ZSTR_VAL(actual);
1543 		fname_len = ZSTR_LEN(actual);
1544 	}
1545 
1546 	ret =  phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, is_data, error);
1547 
1548 	if (actual) {
1549 		zend_string_release_ex(actual, 0);
1550 	}
1551 
1552 	return ret;
1553 }
1554 /* }}}*/
1555 
1556 static inline char *phar_strnstr(const char *buf, int buf_len, const char *search, int search_len) /* {{{ */
1557 {
1558 	const char *c;
1559 	ptrdiff_t so_far = 0;
1560 
1561 	if (buf_len < search_len) {
1562 		return NULL;
1563 	}
1564 
1565 	c = buf - 1;
1566 
1567 	do {
1568 		if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) {
1569 			return (char *) NULL;
1570 		}
1571 
1572 		so_far = c - buf;
1573 
1574 		if (so_far >= (buf_len - search_len)) {
1575 			return (char *) NULL;
1576 		}
1577 
1578 		if (!memcmp(c, search, search_len)) {
1579 			return (char *) c;
1580 		}
1581 	} while (1);
1582 }
1583 /* }}} */
1584 
1585 /**
1586  * Scan an open fp for the required __HALT_COMPILER(); ?> token and verify
1587  * that the manifest is proper, then pass it to phar_parse_pharfile().  SUCCESS
1588  * or FAILURE is returned and pphar is set to a pointer to the phar's manifest
1589  */
1590 static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char *alias, size_t alias_len, uint32_t options, phar_archive_data** pphar, int is_data, char **error) /* {{{ */
1591 {
1592 	const char token[] = "__HALT_COMPILER();";
1593 	const char zip_magic[] = "PK\x03\x04";
1594 	const char gz_magic[] = "\x1f\x8b\x08";
1595 	const char bz_magic[] = "BZh";
1596 	char *pos, test = '\0';
1597 	const int window_size = 1024;
1598 	char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */
1599 	const zend_long readsize = sizeof(buffer) - sizeof(token);
1600 	const zend_long tokenlen = sizeof(token) - 1;
1601 	zend_long halt_offset;
1602 	size_t got;
1603 	uint32_t compression = PHAR_FILE_COMPRESSED_NONE;
1604 
1605 	if (error) {
1606 		*error = NULL;
1607 	}
1608 
1609 	if (-1 == php_stream_rewind(fp)) {
1610 		MAPPHAR_ALLOC_FAIL("cannot rewind phar \"%s\"")
1611 	}
1612 
1613 	buffer[sizeof(buffer)-1] = '\0';
1614 	memset(buffer, 32, sizeof(token));
1615 	halt_offset = 0;
1616 
1617 	/* Maybe it's better to compile the file instead of just searching,  */
1618 	/* but we only want the offset. So we want a .re scanner to find it. */
1619 	while(!php_stream_eof(fp)) {
1620 		if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) {
1621 			MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)")
1622 		}
1623 
1624 		if (!test) {
1625 			test = '\1';
1626 			pos = buffer+tokenlen;
1627 			if (!memcmp(pos, gz_magic, 3)) {
1628 				char err = 0;
1629 				php_stream_filter *filter;
1630 				php_stream *temp;
1631 				/* to properly decompress, we have to tell zlib to look for a zlib or gzip header */
1632 				zval filterparams;
1633 
1634 				if (!PHAR_G(has_zlib)) {
1635 					MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file, enable zlib extension in php.ini")
1636 				}
1637 				array_init(&filterparams);
1638 /* this is defined in zlib's zconf.h */
1639 #ifndef MAX_WBITS
1640 #define MAX_WBITS 15
1641 #endif
1642 				add_assoc_long_ex(&filterparams, "window", sizeof("window") - 1, MAX_WBITS + 32);
1643 
1644 				/* entire file is gzip-compressed, uncompress to temporary file */
1645 				if (!(temp = php_stream_fopen_tmpfile())) {
1646 					MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of gzipped phar archive \"%s\"")
1647 				}
1648 
1649 				php_stream_rewind(fp);
1650 				filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp));
1651 
1652 				if (!filter) {
1653 					err = 1;
1654 					add_assoc_long_ex(&filterparams, "window", sizeof("window") - 1, MAX_WBITS);
1655 					filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp));
1656 					zend_array_destroy(Z_ARR(filterparams));
1657 
1658 					if (!filter) {
1659 						php_stream_close(temp);
1660 						MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1661 					}
1662 				} else {
1663 					zend_array_destroy(Z_ARR(filterparams));
1664 				}
1665 
1666 				php_stream_filter_append(&temp->writefilters, filter);
1667 
1668 				if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1669 					if (err) {
1670 						php_stream_close(temp);
1671 						MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6")
1672 					}
1673 					php_stream_close(temp);
1674 					MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file")
1675 				}
1676 
1677 				php_stream_filter_flush(filter, 1);
1678 				php_stream_filter_remove(filter, 1);
1679 				php_stream_close(fp);
1680 				fp = temp;
1681 				php_stream_rewind(fp);
1682 				compression = PHAR_FILE_COMPRESSED_GZ;
1683 
1684 				/* now, start over */
1685 				test = '\0';
1686 				continue;
1687 			} else if (!memcmp(pos, bz_magic, 3)) {
1688 				php_stream_filter *filter;
1689 				php_stream *temp;
1690 
1691 				if (!PHAR_G(has_bz2)) {
1692 					MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file, enable bz2 extension in php.ini")
1693 				}
1694 
1695 				/* entire file is bzip-compressed, uncompress to temporary file */
1696 				if (!(temp = php_stream_fopen_tmpfile())) {
1697 					MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"")
1698 				}
1699 
1700 				php_stream_rewind(fp);
1701 				filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp));
1702 
1703 				if (!filter) {
1704 					php_stream_close(temp);
1705 					MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\", filter creation failed")
1706 				}
1707 
1708 				php_stream_filter_append(&temp->writefilters, filter);
1709 
1710 				if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) {
1711 					php_stream_close(temp);
1712 					MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file")
1713 				}
1714 
1715 				php_stream_filter_flush(filter, 1);
1716 				php_stream_filter_remove(filter, 1);
1717 				php_stream_close(fp);
1718 				fp = temp;
1719 				php_stream_rewind(fp);
1720 				compression = PHAR_FILE_COMPRESSED_BZ2;
1721 
1722 				/* now, start over */
1723 				test = '\0';
1724 				continue;
1725 			}
1726 
1727 			if (!memcmp(pos, zip_magic, 4)) {
1728 				php_stream_seek(fp, 0, SEEK_END);
1729 				return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error);
1730 			}
1731 
1732 			if (got > 512) {
1733 				if (phar_is_tar(pos, fname)) {
1734 					php_stream_rewind(fp);
1735 					return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, is_data, compression, error);
1736 				}
1737 			}
1738 		}
1739 
1740 		if (got > 0 && (pos = phar_strnstr(buffer, got + sizeof(token), token, sizeof(token)-1)) != NULL) {
1741 			halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */
1742 			return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error);
1743 		}
1744 
1745 		halt_offset += got;
1746 		memmove(buffer, buffer + window_size, tokenlen); /* move the memory buffer by the size of the window */
1747 	}
1748 
1749 	MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)")
1750 }
1751 /* }}} */
1752 
1753 /*
1754  * given the location of the file extension and the start of the file path,
1755  * determine the end of the portion of the path (i.e. /path/to/file.ext/blah
1756  * grabs "/path/to/file.ext" as does the straight /path/to/file.ext),
1757  * stat it to determine if it exists.
1758  * if so, check to see if it is a directory and fail if so
1759  * if not, check to see if its dirname() exists (i.e. "/path/to") and is a directory
1760  * succeed if we are creating the file, otherwise fail.
1761  */
1762 static int phar_analyze_path(const char *fname, const char *ext, size_t ext_len, int for_create) /* {{{ */
1763 {
1764 	php_stream_statbuf ssb;
1765 	char *realpath;
1766 	char *filename = estrndup(fname, (ext - fname) + ext_len);
1767 
1768 	if ((realpath = expand_filepath(filename, NULL))) {
1769 #ifdef PHP_WIN32
1770 		phar_unixify_path_separators(realpath, strlen(realpath));
1771 #endif
1772 		if (zend_hash_str_exists(&(PHAR_G(phar_fname_map)), realpath, strlen(realpath))) {
1773 			efree(realpath);
1774 			efree(filename);
1775 			return SUCCESS;
1776 		}
1777 
1778 		if (PHAR_G(manifest_cached) && zend_hash_str_exists(&cached_phars, realpath, strlen(realpath))) {
1779 			efree(realpath);
1780 			efree(filename);
1781 			return SUCCESS;
1782 		}
1783 		efree(realpath);
1784 	}
1785 
1786 	if (SUCCESS == php_stream_stat_path((char *) filename, &ssb)) {
1787 
1788 		efree(filename);
1789 
1790 		if (ssb.sb.st_mode & S_IFDIR) {
1791 			return FAILURE;
1792 		}
1793 
1794 		if (for_create == 1) {
1795 			return FAILURE;
1796 		}
1797 
1798 		return SUCCESS;
1799 	} else {
1800 		char *slash;
1801 
1802 		if (!for_create) {
1803 			efree(filename);
1804 			return FAILURE;
1805 		}
1806 
1807 		slash = (char *) strrchr(filename, '/');
1808 
1809 		if (slash) {
1810 			*slash = '\0';
1811 		}
1812 
1813 		if (SUCCESS != php_stream_stat_path((char *) filename, &ssb)) {
1814 			if (!slash) {
1815 				if (!(realpath = expand_filepath(filename, NULL))) {
1816 					efree(filename);
1817 					return FAILURE;
1818 				}
1819 #ifdef PHP_WIN32
1820 				phar_unixify_path_separators(realpath, strlen(realpath));
1821 #endif
1822 				slash = strstr(realpath, filename);
1823 				if (slash) {
1824 					slash += ((ext - fname) + ext_len);
1825 					*slash = '\0';
1826 				}
1827 				slash = strrchr(realpath, '/');
1828 
1829 				if (slash) {
1830 					*slash = '\0';
1831 				} else {
1832 					efree(realpath);
1833 					efree(filename);
1834 					return FAILURE;
1835 				}
1836 
1837 				if (SUCCESS != php_stream_stat_path(realpath, &ssb)) {
1838 					efree(realpath);
1839 					efree(filename);
1840 					return FAILURE;
1841 				}
1842 
1843 				efree(realpath);
1844 
1845 				if (ssb.sb.st_mode & S_IFDIR) {
1846 					efree(filename);
1847 					return SUCCESS;
1848 				}
1849 			}
1850 
1851 			efree(filename);
1852 			return FAILURE;
1853 		}
1854 
1855 		efree(filename);
1856 
1857 		if (ssb.sb.st_mode & S_IFDIR) {
1858 			return SUCCESS;
1859 		}
1860 
1861 		return FAILURE;
1862 	}
1863 }
1864 /* }}} */
1865 
1866 /* check for ".phar" in extension */
1867 static int phar_check_str(const char *fname, const char *ext_str, size_t ext_len, int executable, int for_create) /* {{{ */
1868 {
1869 	const char *pos;
1870 
1871 	if (ext_len >= 50) {
1872 		return FAILURE;
1873 	}
1874 	if (executable == 1) {
1875 		/* executable phars must contain ".phar" as a valid extension (phar://.pharmy/oops is invalid) */
1876 		/* (phar://hi/there/.phar/oops is also invalid) */
1877 		pos = strstr(ext_str, ".phar");
1878 
1879 		if (!pos
1880 			|| (pos != ext_str && (*(pos - 1) == '/'))
1881 			|| (ext_len - (pos - ext_str)) < 5
1882 			|| !(pos += 5)
1883 			|| !(*pos == '\0' || *pos == '/' || *pos == '.')) {
1884 			return FAILURE;
1885 		}
1886 		return phar_analyze_path(fname, ext_str, ext_len, for_create);
1887 	}
1888 
1889 	/* data phars need only contain a single non-"." to be valid */
1890 	if (!executable) {
1891 		pos = strstr(ext_str, ".phar");
1892 		if (!(pos && (*(pos - 1) != '/')
1893 					&& (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) && *(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1894 			return phar_analyze_path(fname, ext_str, ext_len, for_create);
1895 		}
1896 	} else {
1897 		if (*(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') {
1898 			return phar_analyze_path(fname, ext_str, ext_len, for_create);
1899 		}
1900 	}
1901 
1902 	return FAILURE;
1903 }
1904 /* }}} */
1905 
1906 /*
1907  * if executable is 1, only returns SUCCESS if the extension is one of the tar/zip .phar extensions
1908  * if executable is 0, it returns SUCCESS only if the filename does *not* contain ".phar" anywhere, and treats
1909  * the first extension as the filename extension
1910  *
1911  * if an extension is found, it sets ext_str to the location of the file extension in filename,
1912  * and ext_len to the length of the extension.
1913  * for urls like "phar://alias/oops" it instead sets ext_len to -1 and returns FAILURE, which tells
1914  * the calling function to use "alias" as the phar alias
1915  *
1916  * the last parameter should be set to tell the thing to assume that filename is the full path, and only to check the
1917  * extension rules, not to iterate.
1918  */
1919 int phar_detect_phar_fname_ext(const char *filename, size_t filename_len, const char **ext_str, size_t *ext_len, int executable, int for_create, int is_complete) /* {{{ */
1920 {
1921 	const char *pos, *slash;
1922 
1923 	*ext_str = NULL;
1924 	*ext_len = 0;
1925 
1926 	if (!filename_len || filename_len == 1) {
1927 		return FAILURE;
1928 	}
1929 
1930 	phar_request_initialize();
1931 	/* first check for alias in first segment */
1932 	pos = memchr(filename, '/', filename_len);
1933 
1934 	if (pos && pos != filename) {
1935 		/* check for url like http:// or phar:// */
1936 		if (*(pos - 1) == ':' && (size_t)(pos - filename) < filename_len - 1 && *(pos + 1) == '/') {
1937 			*ext_len = -2;
1938 			*ext_str = NULL;
1939 			return FAILURE;
1940 		}
1941 		if (zend_hash_str_exists(&(PHAR_G(phar_alias_map)), (char *) filename, pos - filename)) {
1942 			*ext_str = pos;
1943 			*ext_len = -1;
1944 			return FAILURE;
1945 		}
1946 
1947 		if (PHAR_G(manifest_cached) && zend_hash_str_exists(&cached_alias, (char *) filename, pos - filename)) {
1948 			*ext_str = pos;
1949 			*ext_len = -1;
1950 			return FAILURE;
1951 		}
1952 	}
1953 
1954 	if (zend_hash_num_elements(&(PHAR_G(phar_fname_map))) || PHAR_G(manifest_cached)) {
1955 		phar_archive_data *pphar;
1956 
1957 		if (is_complete) {
1958 			if (NULL != (pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), (char *) filename, filename_len))) {
1959 				*ext_str = filename + (filename_len - pphar->ext_len);
1960 woohoo:
1961 				*ext_len = pphar->ext_len;
1962 
1963 				if (executable == 2) {
1964 					return SUCCESS;
1965 				}
1966 
1967 				if (executable == 1 && !pphar->is_data) {
1968 					return SUCCESS;
1969 				}
1970 
1971 				if (!executable && pphar->is_data) {
1972 					return SUCCESS;
1973 				}
1974 
1975 				return FAILURE;
1976 			}
1977 
1978 			if (PHAR_G(manifest_cached) && NULL != (pphar = zend_hash_str_find_ptr(&cached_phars, (char *) filename, filename_len))) {
1979 				*ext_str = filename + (filename_len - pphar->ext_len);
1980 				goto woohoo;
1981 			}
1982 		} else {
1983 			zend_string *str_key;
1984 			zend_ulong unused;
1985 
1986 			for (zend_hash_internal_pointer_reset(&(PHAR_G(phar_fname_map)));
1987 				HASH_KEY_NON_EXISTENT != zend_hash_get_current_key(&(PHAR_G(phar_fname_map)), &str_key, &unused);
1988 				zend_hash_move_forward(&(PHAR_G(phar_fname_map)))
1989 			) {
1990 				if (ZSTR_LEN(str_key) > (uint32_t) filename_len) {
1991 					continue;
1992 				}
1993 
1994 				if (!memcmp(filename, ZSTR_VAL(str_key), ZSTR_LEN(str_key)) && ((uint32_t)filename_len == ZSTR_LEN(str_key)
1995 					|| filename[ZSTR_LEN(str_key)] == '/' || filename[ZSTR_LEN(str_key)] == '\0')) {
1996 					if (NULL == (pphar = zend_hash_get_current_data_ptr(&(PHAR_G(phar_fname_map))))) {
1997 						break;
1998 					}
1999 					*ext_str = filename + (ZSTR_LEN(str_key) - pphar->ext_len);
2000 					goto woohoo;
2001 				}
2002 			}
2003 
2004 			if (PHAR_G(manifest_cached)) {
2005 				for (zend_hash_internal_pointer_reset(&cached_phars);
2006 					HASH_KEY_NON_EXISTENT != zend_hash_get_current_key(&cached_phars, &str_key, &unused);
2007 					zend_hash_move_forward(&cached_phars)
2008 				) {
2009 					if (ZSTR_LEN(str_key) > (uint32_t) filename_len) {
2010 						continue;
2011 					}
2012 
2013 					if (!memcmp(filename, ZSTR_VAL(str_key), ZSTR_LEN(str_key)) && ((uint32_t)filename_len == ZSTR_LEN(str_key)
2014 						|| filename[ZSTR_LEN(str_key)] == '/' || filename[ZSTR_LEN(str_key)] == '\0')) {
2015 						if (NULL == (pphar = zend_hash_get_current_data_ptr(&cached_phars))) {
2016 							break;
2017 						}
2018 						*ext_str = filename + (ZSTR_LEN(str_key) - pphar->ext_len);
2019 						goto woohoo;
2020 					}
2021 				}
2022 			}
2023 		}
2024 	}
2025 
2026 	pos = memchr(filename + 1, '.', filename_len);
2027 next_extension:
2028 	if (!pos) {
2029 		return FAILURE;
2030 	}
2031 
2032 	while (pos != filename && (*(pos - 1) == '/' || *(pos - 1) == '\0')) {
2033 		pos = memchr(pos + 1, '.', filename_len - (pos - filename) - 1);
2034 		if (!pos) {
2035 			return FAILURE;
2036 		}
2037 	}
2038 
2039 	slash = memchr(pos, '/', filename_len - (pos - filename));
2040 
2041 	if (!slash) {
2042 		/* this is a url like "phar://blah.phar" with no directory */
2043 		*ext_str = pos;
2044 		*ext_len = strlen(pos);
2045 
2046 		/* file extension must contain "phar" */
2047 		switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create)) {
2048 			case SUCCESS:
2049 				return SUCCESS;
2050 			case FAILURE:
2051 				/* we are at the end of the string, so we fail */
2052 				return FAILURE;
2053 		}
2054 	}
2055 
2056 	/* we've found an extension that ends at a directory separator */
2057 	*ext_str = pos;
2058 	*ext_len = slash - pos;
2059 
2060 	switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create)) {
2061 		case SUCCESS:
2062 			return SUCCESS;
2063 		case FAILURE:
2064 			/* look for more extensions */
2065 			pos = strchr(pos + 1, '.');
2066 			if (pos) {
2067 				*ext_str = NULL;
2068 				*ext_len = 0;
2069 			}
2070 			goto next_extension;
2071 	}
2072 
2073 	return FAILURE;
2074 }
2075 /* }}} */
2076 
2077 static int php_check_dots(const char *element, size_t n) /* {{{ */
2078 {
2079 	for(n-- ; n != SIZE_MAX; --n) {
2080 		if (element[n] != '.') {
2081 			return 1;
2082 		}
2083 	}
2084 	return 0;
2085 }
2086 /* }}} */
2087 
2088 #define IS_DIRECTORY_UP(element, len) \
2089 	(len >= 2 && !php_check_dots(element, len))
2090 
2091 #define IS_DIRECTORY_CURRENT(element, len) \
2092 	(len == 1 && element[0] == '.')
2093 
2094 #define IS_BACKSLASH(c) ((c) == '/')
2095 
2096 /**
2097  * Remove .. and . references within a phar filename
2098  */
2099 char *phar_fix_filepath(char *path, size_t *new_len, int use_cwd) /* {{{ */
2100 {
2101 	char *newpath;
2102 	size_t newpath_len;
2103 	char *ptr;
2104 	char *tok;
2105 	size_t ptr_length, path_length = *new_len;
2106 
2107 	if (PHAR_G(cwd_len) && use_cwd && path_length > 2 && path[0] == '.' && path[1] == '/') {
2108 		newpath_len = PHAR_G(cwd_len);
2109 		newpath = emalloc(strlen(path) + newpath_len + 1);
2110 		memcpy(newpath, PHAR_G(cwd), newpath_len);
2111 	} else {
2112 		newpath = emalloc(strlen(path) + 2);
2113 		newpath[0] = '/';
2114 		newpath_len = 1;
2115 	}
2116 
2117 	ptr = path;
2118 
2119 	if (*ptr == '/') {
2120 		++ptr;
2121 	}
2122 
2123 	tok = ptr;
2124 
2125 	do {
2126 		ptr = memchr(ptr, '/', path_length - (ptr - path));
2127 	} while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2128 
2129 	if (!ptr && (path_length - (tok - path))) {
2130 		switch (path_length - (tok - path)) {
2131 			case 1:
2132 				if (*tok == '.') {
2133 					efree(path);
2134 					*new_len = 1;
2135 					efree(newpath);
2136 					return estrndup("/", 1);
2137 				}
2138 				break;
2139 			case 2:
2140 				if (tok[0] == '.' && tok[1] == '.') {
2141 					efree(path);
2142 					*new_len = 1;
2143 					efree(newpath);
2144 					return estrndup("/", 1);
2145 				}
2146 		}
2147 		efree(newpath);
2148 		return path;
2149 	}
2150 
2151 	while (ptr) {
2152 		ptr_length = ptr - tok;
2153 last_time:
2154 		if (IS_DIRECTORY_UP(tok, ptr_length)) {
2155 #define PREVIOUS newpath[newpath_len - 1]
2156 
2157 			while (newpath_len > 1 && !IS_BACKSLASH(PREVIOUS)) {
2158 				newpath_len--;
2159 			}
2160 
2161 			if (newpath[0] != '/') {
2162 				newpath[newpath_len] = '\0';
2163 			} else if (newpath_len > 1) {
2164 				--newpath_len;
2165 			}
2166 		} else if (!IS_DIRECTORY_CURRENT(tok, ptr_length)) {
2167 			if (newpath_len > 1) {
2168 				newpath[newpath_len++] = '/';
2169 				memcpy(newpath + newpath_len, tok, ptr_length+1);
2170 			} else {
2171 				memcpy(newpath + newpath_len, tok, ptr_length+1);
2172 			}
2173 
2174 			newpath_len += ptr_length;
2175 		}
2176 
2177 		if (ptr == path + path_length) {
2178 			break;
2179 		}
2180 
2181 		tok = ++ptr;
2182 
2183 		do {
2184 			ptr = memchr(ptr, '/', path_length - (ptr - path));
2185 		} while (ptr && ptr - tok == 0 && *ptr == '/' && ++ptr && ++tok);
2186 
2187 		if (!ptr && (path_length - (tok - path))) {
2188 			ptr_length = path_length - (tok - path);
2189 			ptr = path + path_length;
2190 			goto last_time;
2191 		}
2192 	}
2193 
2194 	efree(path);
2195 	*new_len = newpath_len;
2196 	newpath[newpath_len] = '\0';
2197 	return erealloc(newpath, newpath_len + 1);
2198 }
2199 /* }}} */
2200 
2201 /**
2202  * Process a phar stream name, ensuring we can handle any of:
2203  *
2204  * - whatever.phar
2205  * - whatever.phar.gz
2206  * - whatever.phar.bz2
2207  * - whatever.phar.php
2208  *
2209  * Optionally the name might start with 'phar://'
2210  *
2211  * This is used by phar_parse_url()
2212  */
2213 int phar_split_fname(const char *filename, size_t filename_len, char **arch, size_t *arch_len, char **entry, size_t *entry_len, int executable, int for_create) /* {{{ */
2214 {
2215 	const char *ext_str;
2216 #ifdef PHP_WIN32
2217 	char *save;
2218 #endif
2219 	size_t ext_len;
2220 
2221 	if (CHECK_NULL_PATH(filename, filename_len)) {
2222 		return FAILURE;
2223 	}
2224 
2225 	if (!strncasecmp(filename, "phar://", 7)) {
2226 		filename += 7;
2227 		filename_len -= 7;
2228 	}
2229 
2230 	ext_len = 0;
2231 #ifdef PHP_WIN32
2232 	save = (char *)filename;
2233 	filename = estrndup(filename, filename_len);
2234 	phar_unixify_path_separators((char *)filename, filename_len);
2235 #endif
2236 	if (phar_detect_phar_fname_ext(filename, filename_len, &ext_str, &ext_len, executable, for_create, 0) == FAILURE) {
2237 		if (ext_len != -1) {
2238 			if (!ext_str) {
2239 				/* no / detected, restore arch for error message */
2240 #ifdef PHP_WIN32
2241 				*arch = save;
2242 #else
2243 				*arch = (char*)filename;
2244 #endif
2245 			}
2246 
2247 #ifdef PHP_WIN32
2248 			efree((char *)filename);
2249 #endif
2250 			return FAILURE;
2251 		}
2252 
2253 		ext_len = 0;
2254 		/* no extension detected - instead we are dealing with an alias */
2255 	}
2256 
2257 	*arch_len = ext_str - filename + ext_len;
2258 	*arch = estrndup(filename, *arch_len);
2259 
2260 	if (ext_str[ext_len]) {
2261 		*entry_len = filename_len - *arch_len;
2262 		*entry = estrndup(ext_str+ext_len, *entry_len);
2263 #ifdef PHP_WIN32
2264 		phar_unixify_path_separators(*entry, *entry_len);
2265 #endif
2266 		*entry = phar_fix_filepath(*entry, entry_len, 0);
2267 	} else {
2268 		*entry_len = 1;
2269 		*entry = estrndup("/", 1);
2270 	}
2271 
2272 #ifdef PHP_WIN32
2273 	efree((char *)filename);
2274 #endif
2275 
2276 	return SUCCESS;
2277 }
2278 /* }}} */
2279 
2280 /**
2281  * Invoked when a user calls Phar::mapPhar() from within an executing .phar
2282  * to set up its manifest directly
2283  */
2284 int phar_open_executed_filename(char *alias, size_t alias_len, char **error) /* {{{ */
2285 {
2286 	char *fname;
2287 	php_stream *fp;
2288 	size_t fname_len;
2289 	zend_string *actual = NULL;
2290 	int ret;
2291 
2292 	if (error) {
2293 		*error = NULL;
2294 	}
2295 
2296 	fname = (char*)zend_get_executed_filename();
2297 	fname_len = strlen(fname);
2298 
2299 	if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, 0, REPORT_ERRORS, NULL, 0) == SUCCESS) {
2300 		return SUCCESS;
2301 	}
2302 
2303 	if (!strcmp(fname, "[no active file]")) {
2304 		if (error) {
2305 			spprintf(error, 0, "cannot initialize a phar outside of PHP execution");
2306 		}
2307 		return FAILURE;
2308 	}
2309 
2310 	if (0 == zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1)) {
2311 		if (error) {
2312 			spprintf(error, 0, "__HALT_COMPILER(); must be declared in a phar");
2313 		}
2314 		return FAILURE;
2315 	}
2316 
2317 	if (php_check_open_basedir(fname)) {
2318 		return FAILURE;
2319 	}
2320 
2321 	fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual);
2322 
2323 	if (!fp) {
2324 		if (error) {
2325 			spprintf(error, 0, "unable to open phar for reading \"%s\"", fname);
2326 		}
2327 		if (actual) {
2328 			zend_string_release_ex(actual, 0);
2329 		}
2330 		return FAILURE;
2331 	}
2332 
2333 	if (actual) {
2334 		fname = ZSTR_VAL(actual);
2335 		fname_len = ZSTR_LEN(actual);
2336 	}
2337 
2338 	ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, REPORT_ERRORS, NULL, 0, error);
2339 
2340 	if (actual) {
2341 		zend_string_release_ex(actual, 0);
2342 	}
2343 
2344 	return ret;
2345 }
2346 /* }}} */
2347 
2348 /**
2349  * Validate the CRC32 of a file opened from within the phar
2350  */
2351 int phar_postprocess_file(phar_entry_data *idata, uint32_t crc32, char **error, int process_zip) /* {{{ */
2352 {
2353 	uint32_t crc = ~0;
2354 	int len = idata->internal_file->uncompressed_filesize;
2355 	php_stream *fp = idata->fp;
2356 	phar_entry_info *entry = idata->internal_file;
2357 
2358 	if (error) {
2359 		*error = NULL;
2360 	}
2361 
2362 	if (entry->is_zip && process_zip > 0) {
2363 		/* verify local file header */
2364 		phar_zip_file_header local;
2365 		phar_zip_data_desc desc;
2366 
2367 		if (SUCCESS != phar_open_archive_fp(idata->phar)) {
2368 			spprintf(error, 0, "phar error: unable to open zip-based phar archive \"%s\" to verify local file header for file \"%s\"", idata->phar->fname, entry->filename);
2369 			return FAILURE;
2370 		}
2371 		php_stream_seek(phar_get_entrypfp(idata->internal_file), entry->header_offset, SEEK_SET);
2372 
2373 		if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file), (char *) &local, sizeof(local))) {
2374 
2375 			spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local file header for file \"%s\")", idata->phar->fname, entry->filename);
2376 			return FAILURE;
2377 		}
2378 
2379 		/* check for data descriptor */
2380 		if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) {
2381 			php_stream_seek(phar_get_entrypfp(idata->internal_file),
2382 					entry->header_offset + sizeof(local) +
2383 					PHAR_ZIP_16(local.filename_len) +
2384 					PHAR_ZIP_16(local.extra_len) +
2385 					entry->compressed_filesize, SEEK_SET);
2386 			if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file),
2387 							    (char *) &desc, sizeof(desc))) {
2388 				spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local data descriptor for file \"%s\")", idata->phar->fname, entry->filename);
2389 				return FAILURE;
2390 			}
2391 			if (desc.signature[0] == 'P' && desc.signature[1] == 'K') {
2392 				memcpy(&(local.crc32), &(desc.crc32), 12);
2393 			} else {
2394 				/* old data descriptors have no signature */
2395 				memcpy(&(local.crc32), &desc, 12);
2396 			}
2397 		}
2398 		/* verify local header */
2399 		if (entry->filename_len != PHAR_ZIP_16(local.filename_len) || entry->crc32 != PHAR_ZIP_32(local.crc32) || entry->uncompressed_filesize != PHAR_ZIP_32(local.uncompsize) || entry->compressed_filesize != PHAR_ZIP_32(local.compsize)) {
2400 			spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (local header of file \"%s\" does not match central directory)", idata->phar->fname, entry->filename);
2401 			return FAILURE;
2402 		}
2403 
2404 		/* construct actual offset to file start - local extra_len can be different from central extra_len */
2405 		entry->offset = entry->offset_abs =
2406 			sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len);
2407 
2408 		if (idata->zero && idata->zero != entry->offset_abs) {
2409 			idata->zero = entry->offset_abs;
2410 		}
2411 	}
2412 
2413 	if (process_zip == 1) {
2414 		return SUCCESS;
2415 	}
2416 
2417 	php_stream_seek(fp, idata->zero, SEEK_SET);
2418 
2419 	while (len--) {
2420 		CRC32(crc, php_stream_getc(fp));
2421 	}
2422 
2423 	php_stream_seek(fp, idata->zero, SEEK_SET);
2424 
2425 	if (~crc == crc32) {
2426 		entry->is_crc_checked = 1;
2427 		return SUCCESS;
2428 	} else {
2429 		spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
2430 		return FAILURE;
2431 	}
2432 }
2433 /* }}} */
2434 
2435 static inline void phar_set_32(char *buffer, uint32_t var) /* {{{ */
2436 {
2437 #ifdef WORDS_BIGENDIAN
2438 	*((buffer) + 3) = (unsigned char) (((var) >> 24) & 0xFF);
2439 	*((buffer) + 2) = (unsigned char) (((var) >> 16) & 0xFF);
2440 	*((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF);
2441 	*((buffer) + 0) = (unsigned char) ((var) & 0xFF);
2442 #else
2443 	 memcpy(buffer, &var, sizeof(var));
2444 #endif
2445 } /* }}} */
2446 
2447 static int phar_flush_clean_deleted_apply(zval *zv) /* {{{ */
2448 {
2449 	phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
2450 
2451 	if (entry->fp_refcount <= 0 && entry->is_deleted) {
2452 		return ZEND_HASH_APPLY_REMOVE;
2453 	} else {
2454 		return ZEND_HASH_APPLY_KEEP;
2455 	}
2456 }
2457 /* }}} */
2458 
2459 #include "stub.h"
2460 
2461 zend_string *phar_create_default_stub(const char *index_php, const char *web_index, char **error) /* {{{ */
2462 {
2463 	size_t index_len, web_len;
2464 
2465 	if (error) {
2466 		*error = NULL;
2467 	}
2468 
2469 	if (!index_php) {
2470 		index_php = "index.php";
2471 	}
2472 
2473 	if (!web_index) {
2474 		web_index = "index.php";
2475 	}
2476 
2477 	index_len = strlen(index_php);
2478 	web_len = strlen(web_index);
2479 
2480 	if (index_len > 400) {
2481 		/* ridiculous size not allowed for index.php startup filename */
2482 		if (error) {
2483 			spprintf(error, 0, "Illegal filename passed in for stub creation, was %zd characters long, and only 400 or less is allowed", index_len);
2484 			return NULL;
2485 		}
2486 	}
2487 
2488 	if (web_len > 400) {
2489 		/* ridiculous size not allowed for index.php startup filename */
2490 		if (error) {
2491 			spprintf(error, 0, "Illegal web filename passed in for stub creation, was %zd characters long, and only 400 or less is allowed", web_len);
2492 			return NULL;
2493 		}
2494 	}
2495 
2496 	return phar_get_stub(index_php, web_index, index_len+1, web_len+1);
2497 }
2498 /* }}} */
2499 
2500 /**
2501  * Save phar contents to disk
2502  *
2503  * user_stub contains either a string, or a resource pointer, if len is a negative length.
2504  * user_stub and len should be both 0 if the default or existing stub should be used
2505  */
2506 int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int convert, char **error) /* {{{ */
2507 {
2508 	char halt_stub[] = "__HALT_COMPILER();";
2509 	zend_string *newstub;
2510 	char *tmp;
2511 	phar_entry_info *entry, *newentry;
2512 	size_t halt_offset;
2513 	int restore_alias_len, global_flags = 0, closeoldfile;
2514 	char *pos, has_dirs = 0;
2515 	char manifest[18], entry_buffer[24];
2516 	zend_off_t manifest_ftell;
2517 	zend_long offset;
2518 	size_t wrote;
2519 	uint32_t manifest_len, mytime, loc, new_manifest_count;
2520 	uint32_t newcrc32;
2521 	php_stream *file, *oldfile, *newfile, *stubfile;
2522 	php_stream_filter *filter;
2523 	php_serialize_data_t metadata_hash;
2524 	smart_str main_metadata_str = {0};
2525 	int free_user_stub, free_fp = 1, free_ufp = 1;
2526 	int manifest_hack = 0;
2527 
2528 	if (phar->is_persistent) {
2529 		if (error) {
2530 			spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
2531 		}
2532 		return EOF;
2533 	}
2534 
2535 	if (error) {
2536 		*error = NULL;
2537 	}
2538 
2539 	if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
2540 		return EOF;
2541 	}
2542 
2543 	zend_hash_clean(&phar->virtual_dirs);
2544 
2545 	if (phar->is_zip) {
2546 		return phar_zip_flush(phar, user_stub, len, convert, error);
2547 	}
2548 
2549 	if (phar->is_tar) {
2550 		return phar_tar_flush(phar, user_stub, len, convert, error);
2551 	}
2552 
2553 	if (PHAR_G(readonly)) {
2554 		return EOF;
2555 	}
2556 
2557 	if (phar->fp && !phar->is_brandnew) {
2558 		oldfile = phar->fp;
2559 		closeoldfile = 0;
2560 		php_stream_rewind(oldfile);
2561 	} else {
2562 		oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
2563 		closeoldfile = oldfile != NULL;
2564 	}
2565 	newfile = php_stream_fopen_tmpfile();
2566 	if (!newfile) {
2567 		if (error) {
2568 			spprintf(error, 0, "unable to create temporary file");
2569 		}
2570 		if (closeoldfile) {
2571 			php_stream_close(oldfile);
2572 		}
2573 		return EOF;
2574 	}
2575 
2576 	if (user_stub) {
2577 		zend_string *suser_stub;
2578 		if (len < 0) {
2579 			/* resource passed in */
2580 			if (!(php_stream_from_zval_no_verify(stubfile, (zval *)user_stub))) {
2581 				if (closeoldfile) {
2582 					php_stream_close(oldfile);
2583 				}
2584 				php_stream_close(newfile);
2585 				if (error) {
2586 					spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", phar->fname);
2587 				}
2588 				return EOF;
2589 			}
2590 			if (len == -1) {
2591 				len = PHP_STREAM_COPY_ALL;
2592 			} else {
2593 				len = -len;
2594 			}
2595 			user_stub = 0;
2596 
2597 			if (!(suser_stub = php_stream_copy_to_mem(stubfile, len, 0))) {
2598 				if (closeoldfile) {
2599 					php_stream_close(oldfile);
2600 				}
2601 				php_stream_close(newfile);
2602 				if (error) {
2603 					spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", phar->fname);
2604 				}
2605 				return EOF;
2606 			}
2607 			free_user_stub = 1;
2608 			user_stub = ZSTR_VAL(suser_stub);
2609 			len = ZSTR_LEN(suser_stub);
2610 		} else {
2611 			free_user_stub = 0;
2612 		}
2613 		tmp = estrndup(user_stub, len);
2614 		if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
2615 			efree(tmp);
2616 			if (closeoldfile) {
2617 				php_stream_close(oldfile);
2618 			}
2619 			php_stream_close(newfile);
2620 			if (error) {
2621 				spprintf(error, 0, "illegal stub for phar \"%s\"", phar->fname);
2622 			}
2623 			if (free_user_stub) {
2624 				zend_string_free(suser_stub);
2625 			}
2626 			return EOF;
2627 		}
2628 		pos = user_stub + (pos - tmp);
2629 		efree(tmp);
2630 		len = pos - user_stub + 18;
2631 		if ((size_t)len != php_stream_write(newfile, user_stub, len)
2632 		||			  5 != php_stream_write(newfile, " ?>\r\n", 5)) {
2633 			if (closeoldfile) {
2634 				php_stream_close(oldfile);
2635 			}
2636 			php_stream_close(newfile);
2637 			if (error) {
2638 				spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname);
2639 			}
2640 			if (free_user_stub) {
2641 				zend_string_free(suser_stub);
2642 			}
2643 			return EOF;
2644 		}
2645 		phar->halt_offset = len + 5;
2646 		if (free_user_stub) {
2647 			zend_string_free(suser_stub);
2648 		}
2649 	} else {
2650 		size_t written;
2651 
2652 		if (!user_stub && phar->halt_offset && oldfile && !phar->is_brandnew) {
2653 			php_stream_copy_to_stream_ex(oldfile, newfile, phar->halt_offset, &written);
2654 			newstub = NULL;
2655 		} else {
2656 			/* this is either a brand new phar or a default stub overwrite */
2657 			newstub = phar_create_default_stub(NULL, NULL, NULL);
2658 			phar->halt_offset = ZSTR_LEN(newstub);
2659 			written = php_stream_write(newfile, ZSTR_VAL(newstub), phar->halt_offset);
2660 		}
2661 		if (phar->halt_offset != written) {
2662 			if (closeoldfile) {
2663 				php_stream_close(oldfile);
2664 			}
2665 			php_stream_close(newfile);
2666 			if (error) {
2667 				if (newstub) {
2668 					spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname);
2669 				} else {
2670 					spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname);
2671 				}
2672 			}
2673 			if (newstub) {
2674 				zend_string_free(newstub);
2675 			}
2676 			return EOF;
2677 		}
2678 		if (newstub) {
2679 			zend_string_free(newstub);
2680 		}
2681 	}
2682 	manifest_ftell = php_stream_tell(newfile);
2683 	halt_offset = manifest_ftell;
2684 
2685 	/* Check whether we can get rid of some of the deleted entries which are
2686 	 * unused. However some might still be in use so even after this clean-up
2687 	 * we need to skip entries marked is_deleted. */
2688 	zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply);
2689 
2690 	/* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
2691 	main_metadata_str.s = NULL;
2692 	if (Z_TYPE(phar->metadata) != IS_UNDEF) {
2693 		PHP_VAR_SERIALIZE_INIT(metadata_hash);
2694 		php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash);
2695 		PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2696 	}
2697 	new_manifest_count = 0;
2698 	offset = 0;
2699 	for (zend_hash_internal_pointer_reset(&phar->manifest);
2700 		zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
2701 		zend_hash_move_forward(&phar->manifest)) {
2702 		if ((entry = zend_hash_get_current_data_ptr(&phar->manifest)) == NULL) {
2703 			continue;
2704 		}
2705 		if (entry->cfp) {
2706 			/* did we forget to get rid of cfp last time? */
2707 			php_stream_close(entry->cfp);
2708 			entry->cfp = 0;
2709 		}
2710 		if (entry->is_deleted || entry->is_mounted) {
2711 			/* remove this from the new phar */
2712 			continue;
2713 		}
2714 		if (!entry->is_modified && entry->fp_refcount) {
2715 			/* open file pointers refer to this fp, do not free the stream */
2716 			switch (entry->fp_type) {
2717 				case PHAR_FP:
2718 					free_fp = 0;
2719 					break;
2720 				case PHAR_UFP:
2721 					free_ufp = 0;
2722 				default:
2723 					break;
2724 			}
2725 		}
2726 		/* after excluding deleted files, calculate manifest size in bytes and number of entries */
2727 		++new_manifest_count;
2728 		phar_add_virtual_dirs(phar, entry->filename, entry->filename_len);
2729 
2730 		if (entry->is_dir) {
2731 			/* we use this to calculate API version, 1.1.1 is used for phars with directories */
2732 			has_dirs = 1;
2733 		}
2734 		if (Z_TYPE(entry->metadata) != IS_UNDEF) {
2735 			if (entry->metadata_str.s) {
2736 				smart_str_free(&entry->metadata_str);
2737 			}
2738 			entry->metadata_str.s = NULL;
2739 			PHP_VAR_SERIALIZE_INIT(metadata_hash);
2740 			php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash);
2741 			PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2742 		} else {
2743 			if (entry->metadata_str.s) {
2744 				smart_str_free(&entry->metadata_str);
2745 			}
2746 			entry->metadata_str.s = NULL;
2747 		}
2748 
2749 		/* 32 bits for filename length, length of filename, manifest + metadata, and add 1 for trailing / if a directory */
2750 		offset += 4 + entry->filename_len + sizeof(entry_buffer) + (entry->metadata_str.s ? ZSTR_LEN(entry->metadata_str.s) : 0) + (entry->is_dir ? 1 : 0);
2751 
2752 		/* compress and rehash as necessary */
2753 		if ((oldfile && !entry->is_modified) || entry->is_dir) {
2754 			if (entry->fp_type == PHAR_UFP) {
2755 				/* reset so we can copy the compressed data over */
2756 				entry->fp_type = PHAR_FP;
2757 			}
2758 			continue;
2759 		}
2760 		if (!phar_get_efp(entry, 0)) {
2761 			/* re-open internal file pointer just-in-time */
2762 			newentry = phar_open_jit(phar, entry, error);
2763 			if (!newentry) {
2764 				/* major problem re-opening, so we ignore this file and the error */
2765 				efree(*error);
2766 				*error = NULL;
2767 				continue;
2768 			}
2769 			entry = newentry;
2770 		}
2771 		file = phar_get_efp(entry, 0);
2772 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1)) {
2773 			if (closeoldfile) {
2774 				php_stream_close(oldfile);
2775 			}
2776 			php_stream_close(newfile);
2777 			if (error) {
2778 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2779 			}
2780 			return EOF;
2781 		}
2782 		newcrc32 = ~0;
2783 		mytime = entry->uncompressed_filesize;
2784 		for (loc = 0;loc < mytime; ++loc) {
2785 			CRC32(newcrc32, php_stream_getc(file));
2786 		}
2787 		entry->crc32 = ~newcrc32;
2788 		entry->is_crc_checked = 1;
2789 		if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
2790 			/* not compressed */
2791 			entry->compressed_filesize = entry->uncompressed_filesize;
2792 			continue;
2793 		}
2794 		filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
2795 		if (!filter) {
2796 			if (closeoldfile) {
2797 				php_stream_close(oldfile);
2798 			}
2799 			php_stream_close(newfile);
2800 			if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
2801 				if (error) {
2802 					spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2803 				}
2804 			} else {
2805 				if (error) {
2806 					spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2807 				}
2808 			}
2809 			return EOF;
2810 		}
2811 
2812 		/* create new file that holds the compressed version */
2813 		/* work around inability to specify freedom in write and strictness
2814 		in read count */
2815 		entry->cfp = php_stream_fopen_tmpfile();
2816 		if (!entry->cfp) {
2817 			if (error) {
2818 				spprintf(error, 0, "unable to create temporary file");
2819 			}
2820 			if (closeoldfile) {
2821 				php_stream_close(oldfile);
2822 			}
2823 			php_stream_close(newfile);
2824 			return EOF;
2825 		}
2826 		php_stream_flush(file);
2827 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
2828 			if (closeoldfile) {
2829 				php_stream_close(oldfile);
2830 			}
2831 			php_stream_close(newfile);
2832 			if (error) {
2833 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2834 			}
2835 			return EOF;
2836 		}
2837 		php_stream_filter_append((&entry->cfp->writefilters), filter);
2838 		if (SUCCESS != php_stream_copy_to_stream_ex(file, entry->cfp, entry->uncompressed_filesize, NULL)) {
2839 			if (closeoldfile) {
2840 				php_stream_close(oldfile);
2841 			}
2842 			php_stream_close(newfile);
2843 			if (error) {
2844 				spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2845 			}
2846 			return EOF;
2847 		}
2848 		php_stream_filter_flush(filter, 1);
2849 		php_stream_flush(entry->cfp);
2850 		php_stream_filter_remove(filter, 1);
2851 		php_stream_seek(entry->cfp, 0, SEEK_END);
2852 		entry->compressed_filesize = (uint32_t) php_stream_tell(entry->cfp);
2853 		/* generate crc on compressed file */
2854 		php_stream_rewind(entry->cfp);
2855 		entry->old_flags = entry->flags;
2856 		entry->is_modified = 1;
2857 		global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
2858 	}
2859 	global_flags |= PHAR_HDR_SIGNATURE;
2860 
2861 	/* write out manifest pre-header */
2862 	/*  4: manifest length
2863 	 *  4: manifest entry count
2864 	 *  2: phar version
2865 	 *  4: phar global flags
2866 	 *  4: alias length
2867 	 *  ?: the alias itself
2868 	 *  4: phar metadata length
2869 	 *  ?: phar metadata
2870 	 */
2871 	restore_alias_len = phar->alias_len;
2872 	if (phar->is_temporary_alias) {
2873 		phar->alias_len = 0;
2874 	}
2875 
2876 	manifest_len = offset + phar->alias_len + sizeof(manifest) + (main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2877 	phar_set_32(manifest, manifest_len);
2878 	/* Hack - see bug #65028, add padding byte to the end of the manifest */
2879 	if(manifest[0] == '\r' || manifest[0] == '\n') {
2880 		manifest_len++;
2881 		phar_set_32(manifest, manifest_len);
2882 		manifest_hack = 1;
2883 	}
2884 	phar_set_32(manifest+4, new_manifest_count);
2885 	if (has_dirs) {
2886 		*(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
2887 		*(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
2888 	} else {
2889 		*(manifest + 8) = (unsigned char) (((PHAR_API_VERSION_NODIR) >> 8) & 0xFF);
2890 		*(manifest + 9) = (unsigned char) (((PHAR_API_VERSION_NODIR) & 0xF0));
2891 	}
2892 	phar_set_32(manifest+10, global_flags);
2893 	phar_set_32(manifest+14, phar->alias_len);
2894 
2895 	/* write the manifest header */
2896 	if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
2897 	|| (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) {
2898 
2899 		if (closeoldfile) {
2900 			php_stream_close(oldfile);
2901 		}
2902 
2903 		php_stream_close(newfile);
2904 		phar->alias_len = restore_alias_len;
2905 
2906 		if (error) {
2907 			spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname);
2908 		}
2909 
2910 		return EOF;
2911 	}
2912 
2913 	phar->alias_len = restore_alias_len;
2914 
2915 	phar_set_32(manifest, main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2916 	if (4 != php_stream_write(newfile, manifest, 4) || ((main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0)
2917 	&& ZSTR_LEN(main_metadata_str.s) != php_stream_write(newfile, ZSTR_VAL(main_metadata_str.s), ZSTR_LEN(main_metadata_str.s)))) {
2918 		smart_str_free(&main_metadata_str);
2919 
2920 		if (closeoldfile) {
2921 			php_stream_close(oldfile);
2922 		}
2923 
2924 		php_stream_close(newfile);
2925 		phar->alias_len = restore_alias_len;
2926 
2927 		if (error) {
2928 			spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname);
2929 		}
2930 
2931 		return EOF;
2932 	}
2933 	smart_str_free(&main_metadata_str);
2934 
2935 	/* re-calculate the manifest location to simplify later code */
2936 	manifest_ftell = php_stream_tell(newfile);
2937 
2938 	/* now write the manifest */
2939 	for (zend_hash_internal_pointer_reset(&phar->manifest);
2940 		zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
2941 		zend_hash_move_forward(&phar->manifest)) {
2942 
2943 		if ((entry = zend_hash_get_current_data_ptr(&phar->manifest)) == NULL) {
2944 			continue;
2945 		}
2946 
2947 		if (entry->is_deleted || entry->is_mounted) {
2948 			/* remove this from the new phar if deleted, ignore if mounted */
2949 			continue;
2950 		}
2951 
2952 		if (entry->is_dir) {
2953 			/* add 1 for trailing slash */
2954 			phar_set_32(entry_buffer, entry->filename_len + 1);
2955 		} else {
2956 			phar_set_32(entry_buffer, entry->filename_len);
2957 		}
2958 
2959 		if (4 != php_stream_write(newfile, entry_buffer, 4)
2960 		|| entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len)
2961 		|| (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) {
2962 			if (closeoldfile) {
2963 				php_stream_close(oldfile);
2964 			}
2965 			php_stream_close(newfile);
2966 			if (error) {
2967 				if (entry->is_dir) {
2968 					spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2969 				} else {
2970 					spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2971 				}
2972 			}
2973 			return EOF;
2974 		}
2975 
2976 		/* set the manifest meta-data:
2977 			4: uncompressed filesize
2978 			4: creation timestamp
2979 			4: compressed filesize
2980 			4: crc32
2981 			4: flags
2982 			4: metadata-len
2983 			+: metadata
2984 		*/
2985 		mytime = time(NULL);
2986 		phar_set_32(entry_buffer, entry->uncompressed_filesize);
2987 		phar_set_32(entry_buffer+4, mytime);
2988 		phar_set_32(entry_buffer+8, entry->compressed_filesize);
2989 		phar_set_32(entry_buffer+12, entry->crc32);
2990 		phar_set_32(entry_buffer+16, entry->flags);
2991 		phar_set_32(entry_buffer+20, entry->metadata_str.s ? ZSTR_LEN(entry->metadata_str.s) : 0);
2992 
2993 		if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer))
2994 		|| (entry->metadata_str.s &&
2995 		    ZSTR_LEN(entry->metadata_str.s) != php_stream_write(newfile, ZSTR_VAL(entry->metadata_str.s), ZSTR_LEN(entry->metadata_str.s)))) {
2996 			if (closeoldfile) {
2997 				php_stream_close(oldfile);
2998 			}
2999 
3000 			php_stream_close(newfile);
3001 
3002 			if (error) {
3003 				spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3004 			}
3005 
3006 			return EOF;
3007 		}
3008 	}
3009 	/* Hack - see bug #65028, add padding byte to the end of the manifest */
3010 	if(manifest_hack) {
3011 		if(1 != php_stream_write(newfile, manifest, 1)) {
3012 			if (closeoldfile) {
3013 				php_stream_close(oldfile);
3014 			}
3015 
3016 			php_stream_close(newfile);
3017 
3018 			if (error) {
3019 				spprintf(error, 0, "unable to write manifest padding byte");
3020 			}
3021 
3022 			return EOF;
3023 		}
3024 	}
3025 
3026 	/* now copy the actual file data to the new phar */
3027 	offset = php_stream_tell(newfile);
3028 	for (zend_hash_internal_pointer_reset(&phar->manifest);
3029 		zend_hash_has_more_elements(&phar->manifest) == SUCCESS;
3030 		zend_hash_move_forward(&phar->manifest)) {
3031 
3032 		if ((entry = zend_hash_get_current_data_ptr(&phar->manifest)) == NULL) {
3033 			continue;
3034 		}
3035 
3036 		if (entry->is_deleted || entry->is_dir || entry->is_mounted) {
3037 			continue;
3038 		}
3039 
3040 		if (entry->cfp) {
3041 			file = entry->cfp;
3042 			php_stream_rewind(file);
3043 		} else {
3044 			file = phar_get_efp(entry, 0);
3045 			if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
3046 				if (closeoldfile) {
3047 					php_stream_close(oldfile);
3048 				}
3049 				php_stream_close(newfile);
3050 				if (error) {
3051 					spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3052 				}
3053 				return EOF;
3054 			}
3055 		}
3056 
3057 		if (!file) {
3058 			if (closeoldfile) {
3059 				php_stream_close(oldfile);
3060 			}
3061 			php_stream_close(newfile);
3062 			if (error) {
3063 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3064 			}
3065 			return EOF;
3066 		}
3067 
3068 		/* this will have changed for all files that have either changed compression or been modified */
3069 		entry->offset = entry->offset_abs = offset;
3070 		offset += entry->compressed_filesize;
3071 		if (php_stream_copy_to_stream_ex(file, newfile, entry->compressed_filesize, &wrote) == FAILURE) {
3072 			if (closeoldfile) {
3073 				php_stream_close(oldfile);
3074 			}
3075 
3076 			php_stream_close(newfile);
3077 
3078 			if (error) {
3079 				spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
3080 			}
3081 
3082 			return EOF;
3083 		}
3084 
3085 		entry->is_modified = 0;
3086 
3087 		if (entry->cfp) {
3088 			php_stream_close(entry->cfp);
3089 			entry->cfp = NULL;
3090 		}
3091 
3092 		if (entry->fp_type == PHAR_MOD) {
3093 			/* this fp is in use by a phar_entry_data returned by phar_get_entry_data, it will be closed when the phar_entry_data is phar_entry_delref'ed */
3094 			if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
3095 				php_stream_close(entry->fp);
3096 			}
3097 
3098 			entry->fp = NULL;
3099 			entry->fp_type = PHAR_FP;
3100 		} else if (entry->fp_type == PHAR_UFP) {
3101 			entry->fp_type = PHAR_FP;
3102 		}
3103 	}
3104 
3105 	/* append signature */
3106 	if (global_flags & PHAR_HDR_SIGNATURE) {
3107 		char sig_buf[4];
3108 
3109 		php_stream_rewind(newfile);
3110 
3111 		if (phar->signature) {
3112 			efree(phar->signature);
3113 			phar->signature = NULL;
3114 		}
3115 
3116 		switch(phar->sig_flags) {
3117 #ifndef PHAR_HASH_OK
3118 			case PHAR_SIG_SHA512:
3119 			case PHAR_SIG_SHA256:
3120 				if (closeoldfile) {
3121 					php_stream_close(oldfile);
3122 				}
3123 				php_stream_close(newfile);
3124 				if (error) {
3125 					spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, phar->fname);
3126 				}
3127 				return EOF;
3128 #endif
3129 			default: {
3130 				char *digest = NULL;
3131 				size_t digest_len;
3132 
3133 				if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error)) {
3134 					if (error) {
3135 						char *save = *error;
3136 						spprintf(error, 0, "phar error: unable to write signature: %s", save);
3137 						efree(save);
3138 					}
3139 					if (digest) {
3140 						efree(digest);
3141 					}
3142 					if (closeoldfile) {
3143 						php_stream_close(oldfile);
3144 					}
3145 					php_stream_close(newfile);
3146 					return EOF;
3147 				}
3148 
3149 				php_stream_write(newfile, digest, digest_len);
3150 				efree(digest);
3151 				if (phar->sig_flags == PHAR_SIG_OPENSSL) {
3152 					phar_set_32(sig_buf, digest_len);
3153 					php_stream_write(newfile, sig_buf, 4);
3154 				}
3155 				break;
3156 			}
3157 		}
3158 		phar_set_32(sig_buf, phar->sig_flags);
3159 		php_stream_write(newfile, sig_buf, 4);
3160 		php_stream_write(newfile, "GBMB", 4);
3161 	}
3162 
3163 	/* finally, close the temp file, rename the original phar,
3164 	   move the temp to the old phar, unlink the old phar, and reload it into memory
3165 	*/
3166 	if (phar->fp && free_fp) {
3167 		php_stream_close(phar->fp);
3168 	}
3169 
3170 	if (phar->ufp) {
3171 		if (free_ufp) {
3172 			php_stream_close(phar->ufp);
3173 		}
3174 		phar->ufp = NULL;
3175 	}
3176 
3177 	if (closeoldfile) {
3178 		php_stream_close(oldfile);
3179 	}
3180 
3181 	phar->internal_file_start = halt_offset + manifest_len + 4;
3182 	phar->halt_offset = halt_offset;
3183 	phar->is_brandnew = 0;
3184 
3185 	php_stream_rewind(newfile);
3186 
3187 	if (phar->donotflush) {
3188 		/* deferred flush */
3189 		phar->fp = newfile;
3190 	} else {
3191 		phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
3192 		if (!phar->fp) {
3193 			phar->fp = newfile;
3194 			if (error) {
3195 				spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
3196 			}
3197 			return EOF;
3198 		}
3199 
3200 		if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
3201 			/* to properly compress, we have to tell zlib to add a zlib header */
3202 			zval filterparams;
3203 
3204 			array_init(&filterparams);
3205 			add_assoc_long(&filterparams, "window", MAX_WBITS+16);
3206 			filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp));
3207 			zend_array_destroy(Z_ARR(filterparams));
3208 
3209 			if (!filter) {
3210 				if (error) {
3211 					spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname);
3212 				}
3213 				return EOF;
3214 			}
3215 
3216 			php_stream_filter_append(&phar->fp->writefilters, filter);
3217 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3218 			php_stream_filter_flush(filter, 1);
3219 			php_stream_filter_remove(filter, 1);
3220 			php_stream_close(phar->fp);
3221 			/* use the temp stream as our base */
3222 			phar->fp = newfile;
3223 		} else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
3224 			filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp));
3225 			php_stream_filter_append(&phar->fp->writefilters, filter);
3226 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3227 			php_stream_filter_flush(filter, 1);
3228 			php_stream_filter_remove(filter, 1);
3229 			php_stream_close(phar->fp);
3230 			/* use the temp stream as our base */
3231 			phar->fp = newfile;
3232 		} else {
3233 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3234 			/* we could also reopen the file in "rb" mode but there is no need for that */
3235 			php_stream_close(newfile);
3236 		}
3237 	}
3238 
3239 	if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) {
3240 		if (error) {
3241 			spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname);
3242 		}
3243 		return EOF;
3244 	}
3245 
3246 	return EOF;
3247 }
3248 /* }}} */
3249 
3250 #ifdef COMPILE_DL_PHAR
3251 #ifdef ZTS
3252 ZEND_TSRMLS_CACHE_DEFINE()
3253 #endif
3254 ZEND_GET_MODULE(phar)
3255 #endif
3256 
3257 /* {{{ phar_functions[]
3258  *
3259  * Every user visible function must have an entry in phar_functions[].
3260  */
3261 static const zend_function_entry phar_functions[] = {
3262 	PHP_FE_END
3263 };
3264 /* }}}*/
3265 
3266 static size_t phar_zend_stream_reader(void *handle, char *buf, size_t len) /* {{{ */
3267 {
3268 	return php_stream_read(phar_get_pharfp((phar_archive_data*)handle), buf, len);
3269 }
3270 /* }}} */
3271 
3272 static size_t phar_zend_stream_fsizer(void *handle) /* {{{ */
3273 {
3274 	return ((phar_archive_data*)handle)->halt_offset + 32;
3275 } /* }}} */
3276 
3277 zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type);
3278 #define phar_orig_zend_open zend_stream_open_function
3279 
3280 static zend_string *phar_resolve_path(const char *filename, size_t filename_len)
3281 {
3282 	return phar_find_in_include_path((char *) filename, filename_len, NULL);
3283 }
3284 
3285 static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type) /* {{{ */
3286 {
3287 	zend_op_array *res;
3288 	char *name = NULL;
3289 	int failed;
3290 	phar_archive_data *phar;
3291 
3292 	if (!file_handle || !file_handle->filename) {
3293 		return phar_orig_compile_file(file_handle, type);
3294 	}
3295 	if (strstr(file_handle->filename, ".phar") && !strstr(file_handle->filename, "://")) {
3296 		if (SUCCESS == phar_open_from_filename((char*)file_handle->filename, strlen(file_handle->filename), NULL, 0, 0, &phar, NULL)) {
3297 			if (phar->is_zip || phar->is_tar) {
3298 				zend_file_handle f = *file_handle;
3299 
3300 				/* zip or tar-based phar */
3301 				spprintf(&name, 4096, "phar://%s/%s", file_handle->filename, ".phar/stub.php");
3302 				if (SUCCESS == phar_orig_zend_open((const char *)name, &f)) {
3303 
3304 					efree(name);
3305 					name = NULL;
3306 
3307 					f.filename = file_handle->filename;
3308 					if (f.opened_path) {
3309 						efree(f.opened_path);
3310 					}
3311 					f.opened_path = file_handle->opened_path;
3312 					f.free_filename = file_handle->free_filename;
3313 
3314 					switch (file_handle->type) {
3315 						case ZEND_HANDLE_STREAM:
3316 						case ZEND_HANDLE_MAPPED:
3317 							if (file_handle->handle.stream.closer && file_handle->handle.stream.handle) {
3318 								file_handle->handle.stream.closer(file_handle->handle.stream.handle);
3319 							}
3320 							file_handle->handle.stream.handle = NULL;
3321 							break;
3322 						default:
3323 							break;
3324 					}
3325 					*file_handle = f;
3326 				}
3327 			} else if (phar->flags & PHAR_FILE_COMPRESSION_MASK) {
3328 				zend_file_handle_dtor(file_handle);
3329 				/* compressed phar */
3330 				file_handle->type = ZEND_HANDLE_STREAM;
3331 				/* we do our own reading directly from the phar, don't change the next line */
3332 				file_handle->handle.stream.handle  = phar;
3333 				file_handle->handle.stream.reader  = phar_zend_stream_reader;
3334 				file_handle->handle.stream.closer  = NULL;
3335 				file_handle->handle.stream.fsizer  = phar_zend_stream_fsizer;
3336 				file_handle->handle.stream.isatty  = 0;
3337 				phar->is_persistent ?
3338 					php_stream_rewind(PHAR_G(cached_fp)[phar->phar_pos].fp) :
3339 					php_stream_rewind(phar->fp);
3340 				memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
3341 			}
3342 		}
3343 	}
3344 
3345 	zend_try {
3346 		failed = 0;
3347 		CG(zend_lineno) = 0;
3348 		res = phar_orig_compile_file(file_handle, type);
3349 	} zend_catch {
3350 		failed = 1;
3351 		res = NULL;
3352 	} zend_end_try();
3353 
3354 	if (name) {
3355 		efree(name);
3356 	}
3357 
3358 	if (failed) {
3359 		zend_bailout();
3360 	}
3361 
3362 	return res;
3363 }
3364 /* }}} */
3365 
3366 typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int);
3367 typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr);
3368 
3369 static void mime_type_dtor(zval *zv)
3370 {
3371 	free(Z_PTR_P(zv));
3372 }
3373 
3374 PHP_GINIT_FUNCTION(phar) /* {{{ */
3375 {
3376 #if defined(COMPILE_DL_PHAR) && defined(ZTS)
3377 	ZEND_TSRMLS_CACHE_UPDATE();
3378 #endif
3379 	phar_mime_type mime;
3380 
3381 	memset(phar_globals, 0, sizeof(zend_phar_globals));
3382 	phar_globals->readonly = 1;
3383 
3384 	zend_hash_init(&phar_globals->mime_types, 0, NULL, mime_type_dtor, 1);
3385 
3386 #define PHAR_SET_MIME(mimetype, ret, fileext) \
3387 		mime.mime = mimetype; \
3388 		mime.len = sizeof((mimetype))+1; \
3389 		mime.type = ret; \
3390 		zend_hash_str_add_mem(&phar_globals->mime_types, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type)); \
3391 
3392 	PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps")
3393 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c")
3394 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc")
3395 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp")
3396 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++")
3397 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd")
3398 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h")
3399 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log")
3400 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng")
3401 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt")
3402 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd")
3403 	PHAR_SET_MIME("", PHAR_MIME_PHP, "php")
3404 	PHAR_SET_MIME("", PHAR_MIME_PHP, "inc")
3405 	PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi")
3406 	PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp")
3407 	PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css")
3408 	PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif")
3409 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm")
3410 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html")
3411 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls")
3412 	PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico")
3413 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe")
3414 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg")
3415 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg")
3416 	PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js")
3417 	PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi")
3418 	PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid")
3419 	PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod")
3420 	PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov")
3421 	PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3")
3422 	PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg")
3423 	PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg")
3424 	PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf")
3425 	PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png")
3426 	PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf")
3427 	PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif")
3428 	PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff")
3429 	PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav")
3430 	PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm")
3431 	PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml")
3432 
3433 	phar_restore_orig_functions();
3434 }
3435 /* }}} */
3436 
3437 PHP_GSHUTDOWN_FUNCTION(phar) /* {{{ */
3438 {
3439 	zend_hash_destroy(&phar_globals->mime_types);
3440 }
3441 /* }}} */
3442 
3443 PHP_MINIT_FUNCTION(phar) /* {{{ */
3444 {
3445 	REGISTER_INI_ENTRIES();
3446 
3447 	phar_orig_compile_file = zend_compile_file;
3448 	zend_compile_file = phar_compile_file;
3449 
3450 	phar_save_resolve_path = zend_resolve_path;
3451 	zend_resolve_path = phar_resolve_path;
3452 
3453 	phar_object_init();
3454 
3455 	phar_intercept_functions_init();
3456 	phar_save_orig_functions();
3457 
3458 	return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper);
3459 }
3460 /* }}} */
3461 
3462 PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
3463 {
3464 	php_unregister_url_stream_wrapper("phar");
3465 
3466 	phar_intercept_functions_shutdown();
3467 
3468 	if (zend_compile_file == phar_compile_file) {
3469 		zend_compile_file = phar_orig_compile_file;
3470 	}
3471 
3472 	if (PHAR_G(manifest_cached)) {
3473 		zend_hash_destroy(&(cached_phars));
3474 		zend_hash_destroy(&(cached_alias));
3475 	}
3476 
3477 	return SUCCESS;
3478 }
3479 /* }}} */
3480 
3481 void phar_request_initialize(void) /* {{{ */
3482 {
3483 	if (!PHAR_G(request_init))
3484 	{
3485 		PHAR_G(last_phar) = NULL;
3486 		PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
3487 		PHAR_G(has_bz2) = zend_hash_str_exists(&module_registry, "bz2", sizeof("bz2")-1);
3488 		PHAR_G(has_zlib) = zend_hash_str_exists(&module_registry, "zlib", sizeof("zlib")-1);
3489 		PHAR_G(request_init) = 1;
3490 		PHAR_G(request_ends) = 0;
3491 		PHAR_G(request_done) = 0;
3492 		zend_hash_init(&(PHAR_G(phar_fname_map)), 5, zend_get_hash_value, destroy_phar_data,  0);
3493 		zend_hash_init(&(PHAR_G(phar_persist_map)), 5, zend_get_hash_value, NULL,  0);
3494 		zend_hash_init(&(PHAR_G(phar_alias_map)), 5, zend_get_hash_value, NULL, 0);
3495 
3496 		if (PHAR_G(manifest_cached)) {
3497 			phar_archive_data *pphar;
3498 			phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp));
3499 
3500 			for (zend_hash_internal_pointer_reset(&cached_phars);
3501 			(pphar = zend_hash_get_current_data_ptr(&cached_phars)) != NULL;
3502 			zend_hash_move_forward(&cached_phars)) {
3503 				stuff[pphar->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar->manifest)), sizeof(phar_entry_fp_info));
3504 			}
3505 
3506 			PHAR_G(cached_fp) = stuff;
3507 		}
3508 
3509 		PHAR_G(phar_SERVER_mung_list) = 0;
3510 		PHAR_G(cwd) = NULL;
3511 		PHAR_G(cwd_len) = 0;
3512 		PHAR_G(cwd_init) = 0;
3513 	}
3514 }
3515 /* }}} */
3516 
3517 PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
3518 {
3519 	uint32_t i;
3520 
3521 	PHAR_G(request_ends) = 1;
3522 
3523 	if (PHAR_G(request_init))
3524 	{
3525 		phar_release_functions();
3526 		zend_hash_destroy(&(PHAR_G(phar_alias_map)));
3527 		HT_FLAGS(&PHAR_G(phar_alias_map)) = 0;
3528 		zend_hash_destroy(&(PHAR_G(phar_fname_map)));
3529 		HT_FLAGS(&PHAR_G(phar_fname_map)) = 0;
3530 		zend_hash_destroy(&(PHAR_G(phar_persist_map)));
3531 		HT_FLAGS(&PHAR_G(phar_persist_map)) = 0;
3532 		PHAR_G(phar_SERVER_mung_list) = 0;
3533 
3534 		if (PHAR_G(cached_fp)) {
3535 			for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) {
3536 				if (PHAR_G(cached_fp)[i].fp) {
3537 					php_stream_close(PHAR_G(cached_fp)[i].fp);
3538 				}
3539 				if (PHAR_G(cached_fp)[i].ufp) {
3540 					php_stream_close(PHAR_G(cached_fp)[i].ufp);
3541 				}
3542 				efree(PHAR_G(cached_fp)[i].manifest);
3543 			}
3544 			efree(PHAR_G(cached_fp));
3545 			PHAR_G(cached_fp) = 0;
3546 		}
3547 
3548 		PHAR_G(request_init) = 0;
3549 
3550 		if (PHAR_G(cwd)) {
3551 			efree(PHAR_G(cwd));
3552 		}
3553 
3554 		PHAR_G(cwd) = NULL;
3555 		PHAR_G(cwd_len) = 0;
3556 		PHAR_G(cwd_init) = 0;
3557 	}
3558 
3559 	PHAR_G(request_done) = 1;
3560 	return SUCCESS;
3561 }
3562 /* }}} */
3563 
3564 PHP_MINFO_FUNCTION(phar) /* {{{ */
3565 {
3566 	phar_request_initialize();
3567 	php_info_print_table_start();
3568 	php_info_print_table_header(2, "Phar: PHP Archive support", "enabled");
3569 	php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION);
3570 	php_info_print_table_row(2, "Phar-based phar archives", "enabled");
3571 	php_info_print_table_row(2, "Tar-based phar archives", "enabled");
3572 	php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
3573 
3574 	if (PHAR_G(has_zlib)) {
3575 		php_info_print_table_row(2, "gzip compression", "enabled");
3576 	} else {
3577 		php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)");
3578 	}
3579 
3580 	if (PHAR_G(has_bz2)) {
3581 		php_info_print_table_row(2, "bzip2 compression", "enabled");
3582 	} else {
3583 		php_info_print_table_row(2, "bzip2 compression", "disabled (install ext/bz2)");
3584 	}
3585 #ifdef PHAR_HAVE_OPENSSL
3586 	php_info_print_table_row(2, "Native OpenSSL support", "enabled");
3587 #else
3588 	if (zend_hash_str_exists(&module_registry, "openssl", sizeof("openssl")-1)) {
3589 		php_info_print_table_row(2, "OpenSSL support", "enabled");
3590 	} else {
3591 		php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)");
3592 	}
3593 #endif
3594 	php_info_print_table_end();
3595 
3596 	php_info_print_box_start(0);
3597 	PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
3598 	PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3599 	PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
3600 	PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3601 	PUTS("Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle.");
3602 	php_info_print_box_end();
3603 
3604 	DISPLAY_INI_ENTRIES();
3605 }
3606 /* }}} */
3607 
3608 /* {{{ phar_module_entry
3609  */
3610 static const zend_module_dep phar_deps[] = {
3611 	ZEND_MOD_OPTIONAL("apc")
3612 	ZEND_MOD_OPTIONAL("bz2")
3613 	ZEND_MOD_OPTIONAL("openssl")
3614 	ZEND_MOD_OPTIONAL("zlib")
3615 	ZEND_MOD_OPTIONAL("standard")
3616 #if defined(HAVE_HASH) && !defined(COMPILE_DL_HASH)
3617 	ZEND_MOD_REQUIRED("hash")
3618 #endif
3619 	ZEND_MOD_REQUIRED("spl")
3620 	ZEND_MOD_END
3621 };
3622 
3623 zend_module_entry phar_module_entry = {
3624 	STANDARD_MODULE_HEADER_EX, NULL,
3625 	phar_deps,
3626 	"Phar",
3627 	phar_functions,
3628 	PHP_MINIT(phar),
3629 	PHP_MSHUTDOWN(phar),
3630 	NULL,
3631 	PHP_RSHUTDOWN(phar),
3632 	PHP_MINFO(phar),
3633 	PHP_PHAR_VERSION,
3634 	PHP_MODULE_GLOBALS(phar),   /* globals descriptor */
3635 	PHP_GINIT(phar),            /* globals ctor */
3636 	PHP_GSHUTDOWN(phar),        /* globals dtor */
3637 	NULL,                       /* post deactivate */
3638 	STANDARD_MODULE_PROPERTIES_EX
3639 };
3640 /* }}} */
3641 
3642 /*
3643  * Local variables:
3644  * tab-width: 4
3645  * c-basic-offset: 4
3646  * End:
3647  * vim600: noet sw=4 ts=4 fdm=marker
3648  * vim<600: noet sw=4 ts=4
3649  */
3650