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