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