xref: /PHP-8.3/ext/phar/phar.c (revision 22d25d28)
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 = ((uint32_t)(((unsigned char*)(buffer))[3]) << 24) \
461 		| ((uint32_t)(((unsigned char*)(buffer))[2]) << 16) \
462 		| ((uint32_t)(((unsigned char*)(buffer))[1]) <<  8) \
463 		| ((uint32_t)((unsigned char*)(buffer))[0]); \
464 	(buffer) += 4
465 # define PHAR_GET_16(buffer, var) \
466 	var = ((uint16_t)(((unsigned char*)(buffer))[1]) <<  8) \
467 		| ((uint16_t)((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 	static const char token[] = "__HALT_COMPILER();";
1629 	static const char zip_magic[] = "PK\x03\x04";
1630 	static const char gz_magic[] = "\x1f\x8b\x08";
1631 	static 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 <= 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 	if (error) {
2325 		*error = NULL;
2326 	}
2327 
2328 	zend_string *fname = zend_get_executed_filename_ex();
2329 
2330 	if (!fname) {
2331 		if (error) {
2332 			spprintf(error, 0, "cannot initialize a phar outside of PHP execution");
2333 		}
2334 		return FAILURE;
2335 	}
2336 
2337 	if (phar_open_parsed_phar(ZSTR_VAL(fname), ZSTR_LEN(fname), alias, alias_len, 0, REPORT_ERRORS, NULL, 0) == SUCCESS) {
2338 		return SUCCESS;
2339 	}
2340 
2341 	if (0 == zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1)) {
2342 		if (error) {
2343 			spprintf(error, 0, "__HALT_COMPILER(); must be declared in a phar");
2344 		}
2345 		return FAILURE;
2346 	}
2347 
2348 	if (php_check_open_basedir(ZSTR_VAL(fname))) {
2349 		return FAILURE;
2350 	}
2351 
2352 	zend_string *actual = NULL;
2353 	php_stream *fp;
2354 	fp = php_stream_open_wrapper(ZSTR_VAL(fname), "rb", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, &actual);
2355 
2356 	if (!fp) {
2357 		if (error) {
2358 			spprintf(error, 0, "unable to open phar for reading \"%s\"", ZSTR_VAL(fname));
2359 		}
2360 		if (actual) {
2361 			zend_string_release_ex(actual, 0);
2362 		}
2363 		return FAILURE;
2364 	}
2365 
2366 	if (actual) {
2367 		fname = actual;
2368 	}
2369 
2370 	int ret = phar_open_from_fp(fp, ZSTR_VAL(fname), ZSTR_LEN(fname), alias, alias_len, REPORT_ERRORS, NULL, 0, error);
2371 
2372 	if (actual) {
2373 		zend_string_release_ex(actual, 0);
2374 	}
2375 
2376 	return ret;
2377 }
2378 /* }}} */
2379 
2380 /**
2381  * Validate the CRC32 of a file opened from within the phar
2382  */
phar_postprocess_file(phar_entry_data * idata,uint32_t crc32,char ** error,int process_zip)2383 int phar_postprocess_file(phar_entry_data *idata, uint32_t crc32, char **error, int process_zip) /* {{{ */
2384 {
2385 	uint32_t crc = php_crc32_bulk_init();
2386 	int len = idata->internal_file->uncompressed_filesize, ret;
2387 	php_stream *fp = idata->fp;
2388 	phar_entry_info *entry = idata->internal_file;
2389 
2390 	if (error) {
2391 		*error = NULL;
2392 	}
2393 
2394 	if (entry->is_zip && process_zip > 0) {
2395 		/* verify local file header */
2396 		phar_zip_file_header local;
2397 		phar_zip_data_desc desc;
2398 
2399 		if (SUCCESS != phar_open_archive_fp(idata->phar)) {
2400 			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);
2401 			return FAILURE;
2402 		}
2403 		php_stream_seek(phar_get_entrypfp(idata->internal_file), entry->header_offset, SEEK_SET);
2404 
2405 		if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file), (char *) &local, sizeof(local))) {
2406 
2407 			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);
2408 			return FAILURE;
2409 		}
2410 
2411 		/* check for data descriptor */
2412 		if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) {
2413 			php_stream_seek(phar_get_entrypfp(idata->internal_file),
2414 					entry->header_offset + sizeof(local) +
2415 					PHAR_ZIP_16(local.filename_len) +
2416 					PHAR_ZIP_16(local.extra_len) +
2417 					entry->compressed_filesize, SEEK_SET);
2418 			if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file),
2419 							    (char *) &desc, sizeof(desc))) {
2420 				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);
2421 				return FAILURE;
2422 			}
2423 			if (desc.signature[0] == 'P' && desc.signature[1] == 'K') {
2424 				memcpy(&(local.crc32), &(desc.crc32), 12);
2425 			} else {
2426 				/* old data descriptors have no signature */
2427 				memcpy(&(local.crc32), &desc, 12);
2428 			}
2429 		}
2430 		/* verify local header */
2431 		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)) {
2432 			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);
2433 			return FAILURE;
2434 		}
2435 
2436 		/* construct actual offset to file start - local extra_len can be different from central extra_len */
2437 		entry->offset = entry->offset_abs =
2438 			sizeof(local) + entry->header_offset + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len);
2439 
2440 		if (idata->zero && idata->zero != entry->offset_abs) {
2441 			idata->zero = entry->offset_abs;
2442 		}
2443 	}
2444 
2445 	if (process_zip == 1) {
2446 		return SUCCESS;
2447 	}
2448 
2449 	php_stream_seek(fp, idata->zero, SEEK_SET);
2450 
2451 	ret = php_crc32_stream_bulk_update(&crc, fp, len);
2452 
2453 	php_stream_seek(fp, idata->zero, SEEK_SET);
2454 
2455 	if (SUCCESS == ret && php_crc32_bulk_end(crc) == crc32) {
2456 		entry->is_crc_checked = 1;
2457 		return SUCCESS;
2458 	} else {
2459 		spprintf(error, 0, "phar error: internal corruption of phar \"%s\" (crc32 mismatch on file \"%s\")", idata->phar->fname, entry->filename);
2460 		return FAILURE;
2461 	}
2462 }
2463 /* }}} */
2464 
phar_set_32(char * buffer,uint32_t var)2465 static inline void phar_set_32(char *buffer, uint32_t var) /* {{{ */
2466 {
2467 #ifdef WORDS_BIGENDIAN
2468 	*((buffer) + 3) = (unsigned char) (((var) >> 24) & 0xFF);
2469 	*((buffer) + 2) = (unsigned char) (((var) >> 16) & 0xFF);
2470 	*((buffer) + 1) = (unsigned char) (((var) >> 8) & 0xFF);
2471 	*((buffer) + 0) = (unsigned char) ((var) & 0xFF);
2472 #else
2473 	 memcpy(buffer, &var, sizeof(var));
2474 #endif
2475 } /* }}} */
2476 
phar_flush_clean_deleted_apply(zval * zv)2477 static int phar_flush_clean_deleted_apply(zval *zv) /* {{{ */
2478 {
2479 	phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
2480 
2481 	if (entry->fp_refcount <= 0 && entry->is_deleted) {
2482 		return ZEND_HASH_APPLY_REMOVE;
2483 	} else {
2484 		return ZEND_HASH_APPLY_KEEP;
2485 	}
2486 }
2487 /* }}} */
2488 
2489 #include "stub.h"
2490 
phar_create_default_stub(const char * index_php,const char * web_index,char ** error)2491 zend_string *phar_create_default_stub(const char *index_php, const char *web_index, char **error) /* {{{ */
2492 {
2493 	size_t index_len, web_len;
2494 
2495 	if (error) {
2496 		*error = NULL;
2497 	}
2498 
2499 	if (!index_php) {
2500 		index_php = "index.php";
2501 	}
2502 
2503 	if (!web_index) {
2504 		web_index = "index.php";
2505 	}
2506 
2507 	index_len = strlen(index_php);
2508 	web_len = strlen(web_index);
2509 
2510 	if (index_len > 400) {
2511 		/* ridiculous size not allowed for index.php startup filename */
2512 		if (error) {
2513 			spprintf(error, 0, "Illegal filename passed in for stub creation, was %zd characters long, and only 400 or less is allowed", index_len);
2514 			return NULL;
2515 		}
2516 	}
2517 
2518 	if (web_len > 400) {
2519 		/* ridiculous size not allowed for index.php startup filename */
2520 		if (error) {
2521 			spprintf(error, 0, "Illegal web filename passed in for stub creation, was %zd characters long, and only 400 or less is allowed", web_len);
2522 			return NULL;
2523 		}
2524 	}
2525 
2526 	return phar_get_stub(index_php, web_index, index_len+1, web_len+1);
2527 }
2528 /* }}} */
2529 
2530 /**
2531  * Save phar contents to disk
2532  *
2533  * user_stub contains either a string, or a resource pointer, if len is a negative length.
2534  * user_stub and len should be both 0 if the default or existing stub should be used
2535  */
phar_flush(phar_archive_data * phar,char * user_stub,zend_long len,int convert,char ** error)2536 int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int convert, char **error) /* {{{ */
2537 {
2538 	char halt_stub[] = "__HALT_COMPILER();";
2539 	zend_string *newstub;
2540 	phar_entry_info *entry, *newentry;
2541 	size_t halt_offset;
2542 	int restore_alias_len, global_flags = 0, closeoldfile;
2543 	char *pos, has_dirs = 0;
2544 	char manifest[18], entry_buffer[24];
2545 	zend_off_t manifest_ftell;
2546 	zend_long offset;
2547 	size_t wrote;
2548 	uint32_t manifest_len, mytime, new_manifest_count;
2549 	uint32_t newcrc32;
2550 	php_stream *file, *oldfile, *newfile, *stubfile;
2551 	php_stream_filter *filter;
2552 	php_serialize_data_t metadata_hash;
2553 	smart_str main_metadata_str = {0};
2554 	int free_user_stub, free_fp = 1, free_ufp = 1;
2555 	int manifest_hack = 0;
2556 	php_stream *shared_cfp = NULL;
2557 
2558 	if (phar->is_persistent) {
2559 		if (error) {
2560 			spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
2561 		}
2562 		return EOF;
2563 	}
2564 
2565 	if (error) {
2566 		*error = NULL;
2567 	}
2568 
2569 	if (!zend_hash_num_elements(&phar->manifest) && !user_stub) {
2570 		return EOF;
2571 	}
2572 
2573 	zend_hash_clean(&phar->virtual_dirs);
2574 
2575 	if (phar->is_zip) {
2576 		return phar_zip_flush(phar, user_stub, len, convert, error);
2577 	}
2578 
2579 	if (phar->is_tar) {
2580 		return phar_tar_flush(phar, user_stub, len, convert, error);
2581 	}
2582 
2583 	if (PHAR_G(readonly)) {
2584 		return EOF;
2585 	}
2586 
2587 	if (phar->fp && !phar->is_brandnew) {
2588 		oldfile = phar->fp;
2589 		closeoldfile = 0;
2590 		php_stream_rewind(oldfile);
2591 	} else {
2592 		oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
2593 		closeoldfile = oldfile != NULL;
2594 	}
2595 	newfile = php_stream_fopen_tmpfile();
2596 	if (!newfile) {
2597 		if (error) {
2598 			spprintf(error, 0, "unable to create temporary file");
2599 		}
2600 		if (closeoldfile) {
2601 			php_stream_close(oldfile);
2602 		}
2603 		return EOF;
2604 	}
2605 
2606 	if (user_stub) {
2607 		zend_string *suser_stub;
2608 		if (len < 0) {
2609 			/* resource passed in */
2610 			if (!(php_stream_from_zval_no_verify(stubfile, (zval *)user_stub))) {
2611 				if (closeoldfile) {
2612 					php_stream_close(oldfile);
2613 				}
2614 				php_stream_close(newfile);
2615 				if (error) {
2616 					spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", phar->fname);
2617 				}
2618 				return EOF;
2619 			}
2620 			if (len == -1) {
2621 				len = PHP_STREAM_COPY_ALL;
2622 			} else {
2623 				len = -len;
2624 			}
2625 			user_stub = 0;
2626 
2627 			if (!(suser_stub = php_stream_copy_to_mem(stubfile, len, 0))) {
2628 				if (closeoldfile) {
2629 					php_stream_close(oldfile);
2630 				}
2631 				php_stream_close(newfile);
2632 				if (error) {
2633 					spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", phar->fname);
2634 				}
2635 				return EOF;
2636 			}
2637 			free_user_stub = 1;
2638 			user_stub = ZSTR_VAL(suser_stub);
2639 			len = ZSTR_LEN(suser_stub);
2640 		} else {
2641 			free_user_stub = 0;
2642 		}
2643 		if ((pos = php_stristr(user_stub, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
2644 			if (closeoldfile) {
2645 				php_stream_close(oldfile);
2646 			}
2647 			php_stream_close(newfile);
2648 			if (error) {
2649 				spprintf(error, 0, "illegal stub for phar \"%s\" (__HALT_COMPILER(); is missing)", phar->fname);
2650 			}
2651 			if (free_user_stub) {
2652 				zend_string_free(suser_stub);
2653 			}
2654 			return EOF;
2655 		}
2656 		len = pos - user_stub + 18;
2657 		if ((size_t)len != php_stream_write(newfile, user_stub, len)
2658 		||			  5 != php_stream_write(newfile, " ?>\r\n", 5)) {
2659 			if (closeoldfile) {
2660 				php_stream_close(oldfile);
2661 			}
2662 			php_stream_close(newfile);
2663 			if (error) {
2664 				spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname);
2665 			}
2666 			if (free_user_stub) {
2667 				zend_string_free(suser_stub);
2668 			}
2669 			return EOF;
2670 		}
2671 		phar->halt_offset = len + 5;
2672 		if (free_user_stub) {
2673 			zend_string_free(suser_stub);
2674 		}
2675 	} else {
2676 		size_t written;
2677 
2678 		if (!user_stub && phar->halt_offset && oldfile && !phar->is_brandnew) {
2679 			php_stream_copy_to_stream_ex(oldfile, newfile, phar->halt_offset, &written);
2680 			newstub = NULL;
2681 		} else {
2682 			/* this is either a brand new phar or a default stub overwrite */
2683 			newstub = phar_create_default_stub(NULL, NULL, NULL);
2684 			phar->halt_offset = ZSTR_LEN(newstub);
2685 			written = php_stream_write(newfile, ZSTR_VAL(newstub), phar->halt_offset);
2686 		}
2687 		if (phar->halt_offset != written) {
2688 			if (closeoldfile) {
2689 				php_stream_close(oldfile);
2690 			}
2691 			php_stream_close(newfile);
2692 			if (error) {
2693 				if (newstub) {
2694 					spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname);
2695 				} else {
2696 					spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname);
2697 				}
2698 			}
2699 			if (newstub) {
2700 				zend_string_free(newstub);
2701 			}
2702 			return EOF;
2703 		}
2704 		if (newstub) {
2705 			zend_string_free(newstub);
2706 		}
2707 	}
2708 	manifest_ftell = php_stream_tell(newfile);
2709 	halt_offset = manifest_ftell;
2710 
2711 	/* Check whether we can get rid of some of the deleted entries which are
2712 	 * unused. However some might still be in use so even after this clean-up
2713 	 * we need to skip entries marked is_deleted. */
2714 	zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply);
2715 
2716 	/* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
2717 	main_metadata_str.s = NULL;
2718 	if (phar->metadata_tracker.str) {
2719 		smart_str_appendl(&main_metadata_str, ZSTR_VAL(phar->metadata_tracker.str), ZSTR_LEN(phar->metadata_tracker.str));
2720 	} else if (!Z_ISUNDEF(phar->metadata_tracker.val)) {
2721 		PHP_VAR_SERIALIZE_INIT(metadata_hash);
2722 		php_var_serialize(&main_metadata_str, &phar->metadata_tracker.val, &metadata_hash);
2723 		PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2724 	}
2725 	new_manifest_count = 0;
2726 	offset = 0;
2727 	ZEND_HASH_MAP_FOREACH_PTR(&phar->manifest, entry) {
2728 		if (entry->cfp) {
2729 			/* did we forget to get rid of cfp last time? */
2730 			php_stream_close(entry->cfp);
2731 			entry->cfp = 0;
2732 		}
2733 		if (entry->is_deleted || entry->is_mounted) {
2734 			/* remove this from the new phar */
2735 			continue;
2736 		}
2737 		if (!entry->is_modified && entry->fp_refcount) {
2738 			/* open file pointers refer to this fp, do not free the stream */
2739 			switch (entry->fp_type) {
2740 				case PHAR_FP:
2741 					free_fp = 0;
2742 					break;
2743 				case PHAR_UFP:
2744 					free_ufp = 0;
2745 				default:
2746 					break;
2747 			}
2748 		}
2749 		/* after excluding deleted files, calculate manifest size in bytes and number of entries */
2750 		++new_manifest_count;
2751 		phar_add_virtual_dirs(phar, entry->filename, entry->filename_len);
2752 
2753 		if (entry->is_dir) {
2754 			/* we use this to calculate API version, 1.1.1 is used for phars with directories */
2755 			has_dirs = 1;
2756 		}
2757 		if (!Z_ISUNDEF(entry->metadata_tracker.val) && !entry->metadata_tracker.str) {
2758 			ZEND_ASSERT(!entry->is_persistent);
2759 			/* Assume serialization will succeed. TODO: Set error and throw if EG(exception) != NULL */
2760 			smart_str buf = {0};
2761 			PHP_VAR_SERIALIZE_INIT(metadata_hash);
2762 			php_var_serialize(&buf, &entry->metadata_tracker.val, &metadata_hash);
2763 			PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
2764 			entry->metadata_tracker.str = buf.s;
2765 		}
2766 
2767 		/* 32 bits for filename length, length of filename, manifest + metadata, and add 1 for trailing / if a directory */
2768 		offset += 4 + entry->filename_len + sizeof(entry_buffer) + (entry->metadata_tracker.str ? ZSTR_LEN(entry->metadata_tracker.str) : 0) + (entry->is_dir ? 1 : 0);
2769 
2770 		/* compress and rehash as necessary */
2771 		if ((oldfile && !entry->is_modified) || entry->is_dir) {
2772 			if (entry->fp_type == PHAR_UFP) {
2773 				/* reset so we can copy the compressed data over */
2774 				entry->fp_type = PHAR_FP;
2775 			}
2776 			continue;
2777 		}
2778 		if (!phar_get_efp(entry, 0)) {
2779 			/* re-open internal file pointer just-in-time */
2780 			newentry = phar_open_jit(phar, entry, error);
2781 			if (!newentry) {
2782 				/* major problem re-opening, so we ignore this file and the error */
2783 				efree(*error);
2784 				*error = NULL;
2785 				continue;
2786 			}
2787 			entry = newentry;
2788 		}
2789 		file = phar_get_efp(entry, 0);
2790 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1)) {
2791 			if (closeoldfile) {
2792 				php_stream_close(oldfile);
2793 			}
2794 			php_stream_close(newfile);
2795 			if (error) {
2796 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2797 			}
2798 			return EOF;
2799 		}
2800 		newcrc32 = php_crc32_bulk_init();
2801 		php_crc32_stream_bulk_update(&newcrc32, file, entry->uncompressed_filesize);
2802 		entry->crc32 = php_crc32_bulk_end(newcrc32);
2803 		entry->is_crc_checked = 1;
2804 		if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
2805 			/* not compressed */
2806 			entry->compressed_filesize = entry->uncompressed_filesize;
2807 			continue;
2808 		}
2809 		filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
2810 		if (!filter) {
2811 			if (closeoldfile) {
2812 				php_stream_close(oldfile);
2813 			}
2814 			php_stream_close(newfile);
2815 			if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
2816 				if (error) {
2817 					spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2818 				}
2819 			} else {
2820 				if (error) {
2821 					spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
2822 				}
2823 			}
2824 			return EOF;
2825 		}
2826 
2827 		/* create new file that holds the compressed versions */
2828 		/* work around inability to specify freedom in write and strictness
2829 		in read count */
2830 		if (shared_cfp == NULL) {
2831 			shared_cfp = php_stream_fopen_tmpfile();
2832 		}
2833 		entry->cfp = shared_cfp;
2834 		if (!entry->cfp) {
2835 			if (error) {
2836 				spprintf(error, 0, "unable to create temporary file");
2837 			}
2838 			if (closeoldfile) {
2839 				php_stream_close(oldfile);
2840 			}
2841 			php_stream_close(newfile);
2842 			goto cleanup;
2843 		}
2844 		/* for real phars, header_offset is unused; we misuse it here to store the offset in the temp file */
2845 		ZEND_ASSERT(entry->header_offset == 0);
2846 		entry->header_offset = php_stream_tell(entry->cfp);
2847 		php_stream_flush(file);
2848 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
2849 			if (closeoldfile) {
2850 				php_stream_close(oldfile);
2851 			}
2852 			php_stream_close(newfile);
2853 			if (error) {
2854 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2855 			}
2856 			goto cleanup;
2857 		}
2858 		php_stream_filter_append((&entry->cfp->writefilters), filter);
2859 		if (SUCCESS != php_stream_copy_to_stream_ex(file, entry->cfp, entry->uncompressed_filesize, NULL)) {
2860 			if (closeoldfile) {
2861 				php_stream_close(oldfile);
2862 			}
2863 			php_stream_close(newfile);
2864 			if (error) {
2865 				spprintf(error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
2866 			}
2867 			goto cleanup;
2868 		}
2869 		php_stream_filter_flush(filter, 1);
2870 		php_stream_flush(entry->cfp);
2871 		php_stream_filter_remove(filter, 1);
2872 		php_stream_seek(entry->cfp, 0, SEEK_END);
2873 		entry->compressed_filesize = ((uint32_t) php_stream_tell(entry->cfp)) - entry->header_offset;
2874 		/* generate crc on compressed file */
2875 		entry->old_flags = entry->flags;
2876 		entry->is_modified = 1;
2877 		global_flags |= (entry->flags & PHAR_ENT_COMPRESSION_MASK);
2878 	} ZEND_HASH_FOREACH_END();
2879 	global_flags |= PHAR_HDR_SIGNATURE;
2880 
2881 	/* write out manifest pre-header */
2882 	/*  4: manifest length
2883 	 *  4: manifest entry count
2884 	 *  2: phar version
2885 	 *  4: phar global flags
2886 	 *  4: alias length
2887 	 *  ?: the alias itself
2888 	 *  4: phar metadata length
2889 	 *  ?: phar metadata
2890 	 */
2891 	restore_alias_len = phar->alias_len;
2892 	if (phar->is_temporary_alias) {
2893 		phar->alias_len = 0;
2894 	}
2895 
2896 	manifest_len = offset + phar->alias_len + sizeof(manifest) + (main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2897 	phar_set_32(manifest, manifest_len);
2898 	/* Hack - see bug #65028, add padding byte to the end of the manifest */
2899 	if(manifest[0] == '\r' || manifest[0] == '\n') {
2900 		manifest_len++;
2901 		phar_set_32(manifest, manifest_len);
2902 		manifest_hack = 1;
2903 	}
2904 	phar_set_32(manifest+4, new_manifest_count);
2905 	if (has_dirs) {
2906 		*(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
2907 		*(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0));
2908 	} else {
2909 		*(manifest + 8) = (unsigned char) (((PHAR_API_VERSION_NODIR) >> 8) & 0xFF);
2910 		*(manifest + 9) = (unsigned char) (((PHAR_API_VERSION_NODIR) & 0xF0));
2911 	}
2912 	phar_set_32(manifest+10, global_flags);
2913 	phar_set_32(manifest+14, phar->alias_len);
2914 
2915 	/* write the manifest header */
2916 	if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest))
2917 	|| (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) {
2918 
2919 		if (closeoldfile) {
2920 			php_stream_close(oldfile);
2921 		}
2922 
2923 		php_stream_close(newfile);
2924 		phar->alias_len = restore_alias_len;
2925 
2926 		if (error) {
2927 			spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname);
2928 		}
2929 
2930 		goto cleanup;
2931 	}
2932 
2933 	phar->alias_len = restore_alias_len;
2934 
2935 	phar_set_32(manifest, main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0);
2936 	if (4 != php_stream_write(newfile, manifest, 4) || ((main_metadata_str.s ? ZSTR_LEN(main_metadata_str.s) : 0)
2937 	&& ZSTR_LEN(main_metadata_str.s) != php_stream_write(newfile, ZSTR_VAL(main_metadata_str.s), ZSTR_LEN(main_metadata_str.s)))) {
2938 		smart_str_free(&main_metadata_str);
2939 
2940 		if (closeoldfile) {
2941 			php_stream_close(oldfile);
2942 		}
2943 
2944 		php_stream_close(newfile);
2945 		phar->alias_len = restore_alias_len;
2946 
2947 		if (error) {
2948 			spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname);
2949 		}
2950 
2951 		goto cleanup;
2952 	}
2953 	smart_str_free(&main_metadata_str);
2954 
2955 	/* re-calculate the manifest location to simplify later code */
2956 	manifest_ftell = php_stream_tell(newfile);
2957 
2958 	/* now write the manifest */
2959 	ZEND_HASH_MAP_FOREACH_PTR(&phar->manifest, entry) {
2960 		const zend_string *metadata_str;
2961 		if (entry->is_deleted || entry->is_mounted) {
2962 			/* remove this from the new phar if deleted, ignore if mounted */
2963 			continue;
2964 		}
2965 
2966 		if (entry->is_dir) {
2967 			/* add 1 for trailing slash */
2968 			phar_set_32(entry_buffer, entry->filename_len + 1);
2969 		} else {
2970 			phar_set_32(entry_buffer, entry->filename_len);
2971 		}
2972 
2973 		if (4 != php_stream_write(newfile, entry_buffer, 4)
2974 		|| entry->filename_len != php_stream_write(newfile, entry->filename, entry->filename_len)
2975 		|| (entry->is_dir && 1 != php_stream_write(newfile, "/", 1))) {
2976 			if (closeoldfile) {
2977 				php_stream_close(oldfile);
2978 			}
2979 			php_stream_close(newfile);
2980 			if (error) {
2981 				if (entry->is_dir) {
2982 					spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2983 				} else {
2984 					spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
2985 				}
2986 			}
2987 			goto cleanup;
2988 		}
2989 
2990 		/* set the manifest meta-data:
2991 			4: uncompressed filesize
2992 			4: creation timestamp
2993 			4: compressed filesize
2994 			4: crc32
2995 			4: flags
2996 			4: metadata-len
2997 			+: metadata
2998 		*/
2999 		mytime = time(NULL);
3000 		phar_set_32(entry_buffer, entry->uncompressed_filesize);
3001 		phar_set_32(entry_buffer+4, mytime);
3002 		phar_set_32(entry_buffer+8, entry->compressed_filesize);
3003 		phar_set_32(entry_buffer+12, entry->crc32);
3004 		phar_set_32(entry_buffer+16, entry->flags);
3005 		metadata_str = entry->metadata_tracker.str;
3006 		phar_set_32(entry_buffer+20, metadata_str ? ZSTR_LEN(metadata_str) : 0);
3007 
3008 		if (sizeof(entry_buffer) != php_stream_write(newfile, entry_buffer, sizeof(entry_buffer))
3009 		|| (metadata_str &&
3010 		    ZSTR_LEN(metadata_str) != php_stream_write(newfile, ZSTR_VAL(metadata_str), ZSTR_LEN(metadata_str)))) {
3011 			if (closeoldfile) {
3012 				php_stream_close(oldfile);
3013 			}
3014 
3015 			php_stream_close(newfile);
3016 
3017 			if (error) {
3018 				spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname);
3019 			}
3020 
3021 			goto cleanup;
3022 		}
3023 	} ZEND_HASH_FOREACH_END();
3024 	/* Hack - see bug #65028, add padding byte to the end of the manifest */
3025 	if(manifest_hack) {
3026 		if(1 != php_stream_write(newfile, manifest, 1)) {
3027 			if (closeoldfile) {
3028 				php_stream_close(oldfile);
3029 			}
3030 
3031 			php_stream_close(newfile);
3032 
3033 			if (error) {
3034 				spprintf(error, 0, "unable to write manifest padding byte");
3035 			}
3036 
3037 			goto cleanup;
3038 		}
3039 	}
3040 
3041 	/* now copy the actual file data to the new phar */
3042 	offset = php_stream_tell(newfile);
3043 	ZEND_HASH_MAP_FOREACH_PTR(&phar->manifest, entry) {
3044 		if (entry->is_deleted || entry->is_dir || entry->is_mounted) {
3045 			continue;
3046 		}
3047 
3048 		if (entry->cfp) {
3049 			file = entry->cfp;
3050 			php_stream_seek(file, entry->header_offset, SEEK_SET);
3051 		} else {
3052 			file = phar_get_efp(entry, 0);
3053 			if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
3054 				if (closeoldfile) {
3055 					php_stream_close(oldfile);
3056 				}
3057 				php_stream_close(newfile);
3058 				if (error) {
3059 					spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3060 				}
3061 				goto cleanup;
3062 			}
3063 		}
3064 
3065 		if (!file) {
3066 			if (closeoldfile) {
3067 				php_stream_close(oldfile);
3068 			}
3069 			php_stream_close(newfile);
3070 			if (error) {
3071 				spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname);
3072 			}
3073 			goto cleanup;
3074 		}
3075 
3076 		/* this will have changed for all files that have either changed compression or been modified */
3077 		entry->offset = entry->offset_abs = offset;
3078 		offset += entry->compressed_filesize;
3079 		if (php_stream_copy_to_stream_ex(file, newfile, entry->compressed_filesize, &wrote) == FAILURE) {
3080 			if (closeoldfile) {
3081 				php_stream_close(oldfile);
3082 			}
3083 
3084 			php_stream_close(newfile);
3085 
3086 			if (error) {
3087 				spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname);
3088 			}
3089 
3090 			goto cleanup;
3091 		}
3092 
3093 		entry->is_modified = 0;
3094 
3095 		if (entry->cfp) {
3096 			entry->cfp = NULL;
3097 			entry->header_offset = 0;
3098 		}
3099 
3100 		if (entry->fp_type == PHAR_MOD) {
3101 			/* 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 */
3102 			if (entry->fp_refcount == 0 && entry->fp != phar->fp && entry->fp != phar->ufp) {
3103 				php_stream_close(entry->fp);
3104 			}
3105 
3106 			entry->fp = NULL;
3107 			entry->fp_type = PHAR_FP;
3108 		} else if (entry->fp_type == PHAR_UFP) {
3109 			entry->fp_type = PHAR_FP;
3110 		}
3111 	} ZEND_HASH_FOREACH_END();
3112 
3113 	if (shared_cfp != NULL) {
3114 		php_stream_close(shared_cfp);
3115 		shared_cfp = NULL;
3116 	}
3117 
3118 	/* append signature */
3119 	if (global_flags & PHAR_HDR_SIGNATURE) {
3120 		char sig_buf[4];
3121 
3122 		php_stream_rewind(newfile);
3123 
3124 		if (phar->signature) {
3125 			efree(phar->signature);
3126 			phar->signature = NULL;
3127 		}
3128 
3129 		switch(phar->sig_flags) {
3130 			default: {
3131 				char *digest = NULL;
3132 				size_t digest_len;
3133 
3134 				if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, error)) {
3135 					if (error) {
3136 						char *save = *error;
3137 						spprintf(error, 0, "phar error: unable to write signature: %s", save);
3138 						efree(save);
3139 					}
3140 					if (digest) {
3141 						efree(digest);
3142 					}
3143 					if (closeoldfile) {
3144 						php_stream_close(oldfile);
3145 					}
3146 					php_stream_close(newfile);
3147 					return EOF;
3148 				}
3149 
3150 				php_stream_write(newfile, digest, digest_len);
3151 				efree(digest);
3152 				if (phar->sig_flags == PHAR_SIG_OPENSSL ||
3153 					phar->sig_flags == PHAR_SIG_OPENSSL_SHA256 ||
3154 					phar->sig_flags == PHAR_SIG_OPENSSL_SHA512) {
3155 					phar_set_32(sig_buf, digest_len);
3156 					php_stream_write(newfile, sig_buf, 4);
3157 				}
3158 				break;
3159 			}
3160 		}
3161 		phar_set_32(sig_buf, phar->sig_flags);
3162 		php_stream_write(newfile, sig_buf, 4);
3163 		php_stream_write(newfile, "GBMB", 4);
3164 	}
3165 
3166 	/* finally, close the temp file, rename the original phar,
3167 	   move the temp to the old phar, unlink the old phar, and reload it into memory
3168 	*/
3169 	if (phar->fp && free_fp) {
3170 		php_stream_close(phar->fp);
3171 	}
3172 
3173 	if (phar->ufp) {
3174 		if (free_ufp) {
3175 			php_stream_close(phar->ufp);
3176 		}
3177 		phar->ufp = NULL;
3178 	}
3179 
3180 	if (closeoldfile) {
3181 		php_stream_close(oldfile);
3182 	}
3183 
3184 	phar->internal_file_start = halt_offset + manifest_len + 4;
3185 	phar->halt_offset = halt_offset;
3186 	phar->is_brandnew = 0;
3187 
3188 	php_stream_rewind(newfile);
3189 
3190 	if (phar->donotflush) {
3191 		/* deferred flush */
3192 		phar->fp = newfile;
3193 	} else {
3194 		phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
3195 		if (!phar->fp) {
3196 			phar->fp = newfile;
3197 			if (error) {
3198 				spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
3199 			}
3200 			return EOF;
3201 		}
3202 
3203 		if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
3204 			/* to properly compress, we have to tell zlib to add a zlib header */
3205 			zval filterparams;
3206 
3207 			array_init(&filterparams);
3208 			add_assoc_long(&filterparams, "window", MAX_WBITS+16);
3209 			filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp));
3210 			zend_array_destroy(Z_ARR(filterparams));
3211 
3212 			if (!filter) {
3213 				if (error) {
3214 					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);
3215 				}
3216 				return EOF;
3217 			}
3218 
3219 			php_stream_filter_append(&phar->fp->writefilters, filter);
3220 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3221 			php_stream_filter_flush(filter, 1);
3222 			php_stream_filter_remove(filter, 1);
3223 			php_stream_close(phar->fp);
3224 			/* use the temp stream as our base */
3225 			phar->fp = newfile;
3226 		} else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
3227 			filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp));
3228 			php_stream_filter_append(&phar->fp->writefilters, filter);
3229 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3230 			php_stream_filter_flush(filter, 1);
3231 			php_stream_filter_remove(filter, 1);
3232 			php_stream_close(phar->fp);
3233 			/* use the temp stream as our base */
3234 			phar->fp = newfile;
3235 		} else {
3236 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
3237 			/* we could also reopen the file in "rb" mode but there is no need for that */
3238 			php_stream_close(newfile);
3239 		}
3240 	}
3241 
3242 	if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) {
3243 		if (error) {
3244 			spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname);
3245 		}
3246 		return EOF;
3247 	}
3248 
3249 	return EOF;
3250 
3251 cleanup:
3252 	if (shared_cfp != NULL) {
3253 		php_stream_close(shared_cfp);
3254 	}
3255 	ZEND_HASH_MAP_FOREACH_PTR(&phar->manifest, entry) {
3256 		if (entry->cfp) {
3257 			entry->cfp = NULL;
3258 			entry->header_offset = 0;
3259 		}
3260 	} ZEND_HASH_FOREACH_END();
3261 
3262 	return EOF;
3263 }
3264 /* }}} */
3265 
3266 #ifdef COMPILE_DL_PHAR
3267 #ifdef ZTS
3268 ZEND_TSRMLS_CACHE_DEFINE()
3269 #endif
ZEND_GET_MODULE(phar)3270 ZEND_GET_MODULE(phar)
3271 #endif
3272 
3273 static ssize_t phar_zend_stream_reader(void *handle, char *buf, size_t len) /* {{{ */
3274 {
3275 	return php_stream_read(phar_get_pharfp((phar_archive_data*)handle), buf, len);
3276 }
3277 /* }}} */
3278 
phar_zend_stream_fsizer(void * handle)3279 static size_t phar_zend_stream_fsizer(void *handle) /* {{{ */
3280 {
3281 	return ((phar_archive_data*)handle)->halt_offset + 32;
3282 } /* }}} */
3283 
3284 zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type);
3285 
phar_resolve_path(zend_string * filename)3286 static zend_string *phar_resolve_path(zend_string *filename)
3287 {
3288 	zend_string *ret = phar_find_in_include_path(filename, NULL);
3289 	if (!ret) {
3290 		ret = phar_save_resolve_path(filename);
3291 	}
3292 	return ret;
3293 }
3294 
phar_compile_file(zend_file_handle * file_handle,int type)3295 static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type) /* {{{ */
3296 {
3297 	zend_op_array *res;
3298 	zend_string *name = NULL;
3299 	int failed;
3300 	phar_archive_data *phar;
3301 
3302 	if (!file_handle || !file_handle->filename) {
3303 		return phar_orig_compile_file(file_handle, type);
3304 	}
3305 	if (strstr(ZSTR_VAL(file_handle->filename), ".phar") && !strstr(ZSTR_VAL(file_handle->filename), "://")) {
3306 		if (SUCCESS == phar_open_from_filename(ZSTR_VAL(file_handle->filename), ZSTR_LEN(file_handle->filename), NULL, 0, 0, &phar, NULL)) {
3307 			if (phar->is_zip || phar->is_tar) {
3308 				zend_file_handle f;
3309 
3310 				/* zip or tar-based phar */
3311 				name = zend_strpprintf(4096, "phar://%s/%s", ZSTR_VAL(file_handle->filename), ".phar/stub.php");
3312 				zend_stream_init_filename_ex(&f, name);
3313 				if (SUCCESS == zend_stream_open_function(&f)) {
3314 					zend_string_release(f.filename);
3315 					f.filename = file_handle->filename;
3316 					if (f.opened_path) {
3317 						zend_string_release(f.opened_path);
3318 					}
3319 					f.opened_path = file_handle->opened_path;
3320 
3321 					switch (file_handle->type) {
3322 						case ZEND_HANDLE_STREAM:
3323 							if (file_handle->handle.stream.closer && file_handle->handle.stream.handle) {
3324 								file_handle->handle.stream.closer(file_handle->handle.stream.handle);
3325 							}
3326 							file_handle->handle.stream.handle = NULL;
3327 							break;
3328 						default:
3329 							break;
3330 					}
3331 					*file_handle = f;
3332 				}
3333 			} else if (phar->flags & PHAR_FILE_COMPRESSION_MASK) {
3334 				/* compressed phar */
3335 				file_handle->type = ZEND_HANDLE_STREAM;
3336 				/* we do our own reading directly from the phar, don't change the next line */
3337 				file_handle->handle.stream.handle  = phar;
3338 				file_handle->handle.stream.reader  = phar_zend_stream_reader;
3339 				file_handle->handle.stream.closer  = NULL;
3340 				file_handle->handle.stream.fsizer  = phar_zend_stream_fsizer;
3341 				file_handle->handle.stream.isatty  = 0;
3342 				phar->is_persistent ?
3343 					php_stream_rewind(PHAR_G(cached_fp)[phar->phar_pos].fp) :
3344 					php_stream_rewind(phar->fp);
3345 			}
3346 		}
3347 	}
3348 
3349 	zend_try {
3350 		failed = 0;
3351 		CG(zend_lineno) = 0;
3352 		res = phar_orig_compile_file(file_handle, type);
3353 	} zend_catch {
3354 		failed = 1;
3355 		res = NULL;
3356 	} zend_end_try();
3357 
3358 	if (name) {
3359 		zend_string_release(name);
3360 	}
3361 
3362 	if (failed) {
3363 		zend_bailout();
3364 	}
3365 
3366 	return res;
3367 }
3368 /* }}} */
3369 
3370 typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int);
3371 typedef zend_compile_t* (compile_hook)(zend_compile_t *ptr);
3372 
mime_type_dtor(zval * zv)3373 static void mime_type_dtor(zval *zv)
3374 {
3375 	free(Z_PTR_P(zv));
3376 }
3377 
PHP_GINIT_FUNCTION(phar)3378 PHP_GINIT_FUNCTION(phar) /* {{{ */
3379 {
3380 #if defined(COMPILE_DL_PHAR) && defined(ZTS)
3381 	ZEND_TSRMLS_CACHE_UPDATE();
3382 #endif
3383 	phar_mime_type mime;
3384 
3385 	memset(phar_globals, 0, sizeof(zend_phar_globals));
3386 	HT_INVALIDATE(&phar_globals->phar_persist_map);
3387 	HT_INVALIDATE(&phar_globals->phar_fname_map);
3388 	HT_INVALIDATE(&phar_globals->phar_alias_map);
3389 	phar_globals->readonly = 1;
3390 
3391 	zend_hash_init(&phar_globals->mime_types, 0, NULL, mime_type_dtor, 1);
3392 
3393 #define PHAR_SET_MIME(mimetype, ret, fileext) \
3394 		mime.mime = mimetype; \
3395 		mime.len = sizeof((mimetype))+1; \
3396 		mime.type = ret; \
3397 		zend_hash_str_add_mem(&phar_globals->mime_types, fileext, sizeof(fileext)-1, (void *)&mime, sizeof(phar_mime_type)); \
3398 
3399 	PHAR_SET_MIME("text/html", PHAR_MIME_PHPS, "phps")
3400 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c")
3401 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cc")
3402 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "cpp")
3403 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "c++")
3404 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "dtd")
3405 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "h")
3406 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "log")
3407 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "rng")
3408 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "txt")
3409 	PHAR_SET_MIME("text/plain", PHAR_MIME_OTHER, "xsd")
3410 	PHAR_SET_MIME("", PHAR_MIME_PHP, "php")
3411 	PHAR_SET_MIME("", PHAR_MIME_PHP, "inc")
3412 	PHAR_SET_MIME("video/avi", PHAR_MIME_OTHER, "avi")
3413 	PHAR_SET_MIME("image/bmp", PHAR_MIME_OTHER, "bmp")
3414 	PHAR_SET_MIME("text/css", PHAR_MIME_OTHER, "css")
3415 	PHAR_SET_MIME("image/gif", PHAR_MIME_OTHER, "gif")
3416 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htm")
3417 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "html")
3418 	PHAR_SET_MIME("text/html", PHAR_MIME_OTHER, "htmls")
3419 	PHAR_SET_MIME("image/x-ico", PHAR_MIME_OTHER, "ico")
3420 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpe")
3421 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpg")
3422 	PHAR_SET_MIME("image/jpeg", PHAR_MIME_OTHER, "jpeg")
3423 	PHAR_SET_MIME("application/x-javascript", PHAR_MIME_OTHER, "js")
3424 	PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "midi")
3425 	PHAR_SET_MIME("audio/midi", PHAR_MIME_OTHER, "mid")
3426 	PHAR_SET_MIME("audio/mod", PHAR_MIME_OTHER, "mod")
3427 	PHAR_SET_MIME("movie/quicktime", PHAR_MIME_OTHER, "mov")
3428 	PHAR_SET_MIME("audio/mp3", PHAR_MIME_OTHER, "mp3")
3429 	PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpg")
3430 	PHAR_SET_MIME("video/mpeg", PHAR_MIME_OTHER, "mpeg")
3431 	PHAR_SET_MIME("application/pdf", PHAR_MIME_OTHER, "pdf")
3432 	PHAR_SET_MIME("image/png", PHAR_MIME_OTHER, "png")
3433 	PHAR_SET_MIME("application/shockwave-flash", PHAR_MIME_OTHER, "swf")
3434 	PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tif")
3435 	PHAR_SET_MIME("image/tiff", PHAR_MIME_OTHER, "tiff")
3436 	PHAR_SET_MIME("audio/wav", PHAR_MIME_OTHER, "wav")
3437 	PHAR_SET_MIME("image/xbm", PHAR_MIME_OTHER, "xbm")
3438 	PHAR_SET_MIME("text/xml", PHAR_MIME_OTHER, "xml")
3439 
3440 	phar_restore_orig_functions();
3441 }
3442 /* }}} */
3443 
PHP_GSHUTDOWN_FUNCTION(phar)3444 PHP_GSHUTDOWN_FUNCTION(phar) /* {{{ */
3445 {
3446 	zend_hash_destroy(&phar_globals->mime_types);
3447 }
3448 /* }}} */
3449 
PHP_MINIT_FUNCTION(phar)3450 PHP_MINIT_FUNCTION(phar) /* {{{ */
3451 {
3452 	REGISTER_INI_ENTRIES();
3453 
3454 	phar_orig_compile_file = zend_compile_file;
3455 	zend_compile_file = phar_compile_file;
3456 
3457 	phar_save_resolve_path = zend_resolve_path;
3458 	zend_resolve_path = phar_resolve_path;
3459 
3460 	phar_object_init();
3461 
3462 	phar_intercept_functions_init();
3463 	phar_save_orig_functions();
3464 
3465 	return php_register_url_stream_wrapper("phar", &php_stream_phar_wrapper);
3466 }
3467 /* }}} */
3468 
PHP_MSHUTDOWN_FUNCTION(phar)3469 PHP_MSHUTDOWN_FUNCTION(phar) /* {{{ */
3470 {
3471 	php_unregister_url_stream_wrapper("phar");
3472 
3473 	phar_intercept_functions_shutdown();
3474 
3475 	if (zend_compile_file == phar_compile_file) {
3476 		zend_compile_file = phar_orig_compile_file;
3477 	}
3478 
3479 	if (PHAR_G(manifest_cached)) {
3480 		zend_hash_destroy(&(cached_phars));
3481 		zend_hash_destroy(&(cached_alias));
3482 	}
3483 
3484 	UNREGISTER_INI_ENTRIES();
3485 	return SUCCESS;
3486 }
3487 /* }}} */
3488 
phar_request_initialize(void)3489 void phar_request_initialize(void) /* {{{ */
3490 {
3491 	if (!PHAR_G(request_init))
3492 	{
3493 		PHAR_G(last_phar) = NULL;
3494 		PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
3495 		PHAR_G(has_bz2) = zend_hash_str_exists(&module_registry, "bz2", sizeof("bz2")-1);
3496 		PHAR_G(has_zlib) = zend_hash_str_exists(&module_registry, "zlib", sizeof("zlib")-1);
3497 		PHAR_G(request_init) = 1;
3498 		PHAR_G(request_ends) = 0;
3499 		PHAR_G(request_done) = 0;
3500 		zend_hash_init(&(PHAR_G(phar_fname_map)), 5, zend_get_hash_value, destroy_phar_data,  0);
3501 		zend_hash_init(&(PHAR_G(phar_persist_map)), 5, zend_get_hash_value, NULL,  0);
3502 		zend_hash_init(&(PHAR_G(phar_alias_map)), 5, zend_get_hash_value, NULL, 0);
3503 
3504 		if (PHAR_G(manifest_cached)) {
3505 			phar_archive_data *pphar;
3506 			phar_entry_fp *stuff = (phar_entry_fp *) ecalloc(zend_hash_num_elements(&cached_phars), sizeof(phar_entry_fp));
3507 
3508 			ZEND_HASH_MAP_FOREACH_PTR(&cached_phars, pphar) {
3509 				stuff[pphar->phar_pos].manifest = (phar_entry_fp_info *) ecalloc( zend_hash_num_elements(&(pphar->manifest)), sizeof(phar_entry_fp_info));
3510 			} ZEND_HASH_FOREACH_END();
3511 
3512 			PHAR_G(cached_fp) = stuff;
3513 		}
3514 
3515 		PHAR_G(phar_SERVER_mung_list) = 0;
3516 		PHAR_G(cwd) = NULL;
3517 		PHAR_G(cwd_len) = 0;
3518 		PHAR_G(cwd_init) = 0;
3519 	}
3520 }
3521 /* }}} */
3522 
PHP_RSHUTDOWN_FUNCTION(phar)3523 PHP_RSHUTDOWN_FUNCTION(phar) /* {{{ */
3524 {
3525 	uint32_t i;
3526 
3527 	PHAR_G(request_ends) = 1;
3528 
3529 	if (PHAR_G(request_init))
3530 	{
3531 		phar_release_functions();
3532 		zend_hash_destroy(&(PHAR_G(phar_alias_map)));
3533 		HT_INVALIDATE(&PHAR_G(phar_alias_map));
3534 		zend_hash_destroy(&(PHAR_G(phar_fname_map)));
3535 		HT_INVALIDATE(&PHAR_G(phar_fname_map));
3536 		zend_hash_destroy(&(PHAR_G(phar_persist_map)));
3537 		HT_INVALIDATE(&PHAR_G(phar_persist_map));
3538 		PHAR_G(phar_SERVER_mung_list) = 0;
3539 
3540 		if (PHAR_G(cached_fp)) {
3541 			for (i = 0; i < zend_hash_num_elements(&cached_phars); ++i) {
3542 				if (PHAR_G(cached_fp)[i].fp) {
3543 					php_stream_close(PHAR_G(cached_fp)[i].fp);
3544 				}
3545 				if (PHAR_G(cached_fp)[i].ufp) {
3546 					php_stream_close(PHAR_G(cached_fp)[i].ufp);
3547 				}
3548 				efree(PHAR_G(cached_fp)[i].manifest);
3549 			}
3550 			efree(PHAR_G(cached_fp));
3551 			PHAR_G(cached_fp) = 0;
3552 		}
3553 
3554 		PHAR_G(request_init) = 0;
3555 
3556 		if (PHAR_G(cwd)) {
3557 			efree(PHAR_G(cwd));
3558 		}
3559 
3560 		PHAR_G(cwd) = NULL;
3561 		PHAR_G(cwd_len) = 0;
3562 		PHAR_G(cwd_init) = 0;
3563 	}
3564 
3565 	PHAR_G(request_done) = 1;
3566 	return SUCCESS;
3567 }
3568 /* }}} */
3569 
PHP_MINFO_FUNCTION(phar)3570 PHP_MINFO_FUNCTION(phar) /* {{{ */
3571 {
3572 	phar_request_initialize();
3573 	php_info_print_table_start();
3574 	php_info_print_table_row(2, "Phar: PHP Archive support", "enabled");
3575 	php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION);
3576 	php_info_print_table_row(2, "Phar-based phar archives", "enabled");
3577 	php_info_print_table_row(2, "Tar-based phar archives", "enabled");
3578 	php_info_print_table_row(2, "ZIP-based phar archives", "enabled");
3579 
3580 	if (PHAR_G(has_zlib)) {
3581 		php_info_print_table_row(2, "gzip compression", "enabled");
3582 	} else {
3583 		php_info_print_table_row(2, "gzip compression", "disabled (install ext/zlib)");
3584 	}
3585 
3586 	if (PHAR_G(has_bz2)) {
3587 		php_info_print_table_row(2, "bzip2 compression", "enabled");
3588 	} else {
3589 		php_info_print_table_row(2, "bzip2 compression", "disabled (install ext/bz2)");
3590 	}
3591 #ifdef PHAR_HAVE_OPENSSL
3592 	php_info_print_table_row(2, "Native OpenSSL support", "enabled");
3593 #else
3594 	if (zend_hash_str_exists(&module_registry, "openssl", sizeof("openssl")-1)) {
3595 		php_info_print_table_row(2, "OpenSSL support", "enabled");
3596 	} else {
3597 		php_info_print_table_row(2, "OpenSSL support", "disabled (install ext/openssl)");
3598 	}
3599 #endif
3600 	php_info_print_table_end();
3601 
3602 	php_info_print_box_start(0);
3603 	PUTS("Phar based on pear/PHP_Archive, original concept by Davey Shafik.");
3604 	PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3605 	PUTS("Phar fully realized by Gregory Beaver and Marcus Boerger.");
3606 	PUTS(!sapi_module.phpinfo_as_text?"<br />":"\n");
3607 	PUTS("Portions of tar implementation Copyright (c) 2003-2009 Tim Kientzle.");
3608 	php_info_print_box_end();
3609 
3610 	DISPLAY_INI_ENTRIES();
3611 }
3612 /* }}} */
3613 
3614 /* {{{ phar_module_entry */
3615 static const zend_module_dep phar_deps[] = {
3616 	ZEND_MOD_OPTIONAL("apc")
3617 	ZEND_MOD_OPTIONAL("bz2")
3618 	ZEND_MOD_OPTIONAL("openssl")
3619 	ZEND_MOD_OPTIONAL("zlib")
3620 	ZEND_MOD_OPTIONAL("standard")
3621 	ZEND_MOD_REQUIRED("hash")
3622 	ZEND_MOD_REQUIRED("spl")
3623 	ZEND_MOD_END
3624 };
3625 
3626 zend_module_entry phar_module_entry = {
3627 	STANDARD_MODULE_HEADER_EX, NULL,
3628 	phar_deps,
3629 	"Phar",
3630 	NULL,
3631 	PHP_MINIT(phar),
3632 	PHP_MSHUTDOWN(phar),
3633 	NULL,
3634 	PHP_RSHUTDOWN(phar),
3635 	PHP_MINFO(phar),
3636 	PHP_PHAR_VERSION,
3637 	PHP_MODULE_GLOBALS(phar),   /* globals descriptor */
3638 	PHP_GINIT(phar),            /* globals ctor */
3639 	PHP_GSHUTDOWN(phar),        /* globals dtor */
3640 	NULL,                       /* post deactivate */
3641 	STANDARD_MODULE_PROPERTIES_EX
3642 };
3643 /* }}} */
3644