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